{"_id":"@balavishnuvj/remix-seo","_rev":"3-6573d229da1241914d9334e01e03defc","name":"@balavishnuvj/remix-seo","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@balavishnuvj/remix-seo","version":"1.0.0","description":"Collection of SEO utilities like sitemap, robots.txt, etc. for a Remix Application","main":"./build/index.js","private":false,"scripts":{"build":"tsc --project tsconfig.json","start":"tsc -w --project tsconfig.json","test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/balavishnuvj/remix-seo.git"},"keywords":["remix","sitemap","sitemap.xml","seo","robot.txt"],"author":{"name":"Balavishnu V J","email":"hello@balavishnuvj.com","url":"https://balavishnuvj.com"},"license":"MIT","bugs":{"url":"https://github.com/balavishnuvj/remix-seo/issues"},"homepage":"https://github.com/balavishnuvj/remix-seo#readme","devDependencies":{"@types/lodash":"^4.14.178","@types/node":"^17.0.13","typescript":"^4.5.5"},"peerDependencies":{"@remix-run/react":"^1.0.0","@remix-run/server-runtime":"^1.0.0"},"dependencies":{"lodash":"^4.17.21"},"types":"./build/index.d.ts","gitHead":"16b3c46bb4c9bff2f5293b629344470562fd04d7","_id":"@balavishnuvj/remix-seo@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-Vr3kHaxcW3Ixfi4Gp9UtzfMBgh3VGURe+4YocXgW3J0/XZL8CQGPLF/P/LJk5ZF0jky6/ZM2B+1bf1NMRO6v4w==","shasum":"fbbdb16aaafe08d699b81fb3606524be2461f578","tarball":"https://registry.npmjs.org/@balavishnuvj/remix-seo/-/remix-seo-1.0.0.tgz","fileCount":10,"unpackedSize":24843,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh9/jHCRA9TVsSAnZWagAAo5AP/1HedQObZi4SZYipcwoN\nACcPyh/lHzjmIEg0R22911+6f0FHZ4W42oLqXcj440md3g8Z/9tcyv3dogbD\nFSudjWsbq9jhy4psh/J3zwogwZWHoMiYwrxAqIU4bF/ipy5mQ5LWYK9Lm5Ya\nhW4NZ15nRIyQZ8Ja3RInwwgH7S8w6u8TNHrCsYBNu/+WOL9MMSY+xVKxtUOj\n9p/1lA2DUVpVJ3SMC+U5KeLgh9aIyG28/X1mAqZYlMw95wTM6FpNMgNWNsl8\nEwSStA/U3kUZ7n7hrPvq/d2OSZOKuxb2a98QFmdrHN7+HTaGv41ahkh3v+1q\nacQEqgCTaWq5WDtQBXXtDfgm8/pnmOby0wlyisyTECs42B/Ehdfl+31+8r0J\n3NEDHfmHCMAU6n++B8mSo8f4P7iXpTshzDGwhyhwcP5CVfUV7Un6eUf+cBjn\no6EmKbfR6mjzv0++k0EulO34FubGlqhfXbbpULJ7XVo2PE8d8WPqlDRH6Apx\ntnysDvDFa2Y45JAtJdCLC56bMXCBRzIfBe5RBT87ZqfgybFMq8ShDfaBm/kT\n3wdEHP/twPtPjikTxfhl1cUDKAq9ltvyDp8mxKRtEA1vx1r5ruZy9n4hzT3y\n1ElNBRvEC3KO2PHKR884PEzTZNkz49hmFpFfrPRMmx5JWgh59mTCzrHjKlT/\nWa9v\r\n=i2Mr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBW47gjAz3X1teAeTZZ7sLm8Vxa3GDJ+5qHTwVmDrUnJAiEA936bF5b1iwf6TBx6fh2BtrJw3U9459HZFER4NMo06jU="}]},"_npmUser":{"name":"balavishnuvj","email":"balavishnu.06@gmail.com"},"directories":{},"maintainers":[{"name":"balavishnuvj","email":"balavishnu.06@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/remix-seo_1.0.0_1643641031093_0.8885620765637743"},"_hasShrinkwrap":false},"1.0.1":{"name":"@balavishnuvj/remix-seo","version":"1.0.1","description":"Collection of SEO utilities like sitemap, robots.txt, etc. for a Remix Application","main":"./build/index.js","private":false,"scripts":{"build":"tsc --project tsconfig.json","start":"tsc -w --project tsconfig.json","test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/balavishnuvj/remix-seo.git"},"keywords":["remix","sitemap","sitemap.xml","seo","robot.txt"],"author":{"name":"Balavishnu V J","email":"hello@balavishnuvj.com","url":"https://balavishnuvj.com"},"license":"MIT","bugs":{"url":"https://github.com/balavishnuvj/remix-seo/issues"},"homepage":"https://github.com/balavishnuvj/remix-seo#readme","devDependencies":{"@types/lodash":"^4.14.178","@types/node":"^17.0.13","typescript":"^4.5.5"},"peerDependencies":{"@remix-run/react":"^1.0.0","@remix-run/server-runtime":"^1.0.0"},"dependencies":{"lodash":"^4.17.21"},"types":"./build/index.d.ts","gitHead":"16b3c46bb4c9bff2f5293b629344470562fd04d7","_id":"@balavishnuvj/remix-seo@1.0.1","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-cjklimT245VqlL9UpqCfhdhoPE3psK0fc3SI/oi1mrQDwjCxTnMNKgheDxgm2jpJwoOSayxlIc8w9eqS86MZLA==","shasum":"9788df04a70f14c1a286c0d2c515ce87cf24d01c","tarball":"https://registry.npmjs.org/@balavishnuvj/remix-seo/-/remix-seo-1.0.1.tgz","fileCount":32,"unpackedSize":41093,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh9/mtCRA9TVsSAnZWagAAieAP/0POZ+uOO2M/IGU02EE/\n9QvBuERclmxbkMdMsYtdlTyMHjme/Z8S4A8PKCOnoIzBOvjE4IM+Se1rKDJP\nt9SIu+apfbRcqiLZIF2EqiX8Yg+SCW0RzCWRoFipqfhGh5Qj9Eh1O4ocKlLI\n5jLMqZo72iT6P1+zsCUR70t8zYE5srgs2Tw2+AajpVt0KYWQERBAF7At2sCT\nDz7KLdX06RmgjmGbdk0nEC1oiqhneIficVzqHwpb7b1i8ikeXmqoaROsSeln\n5Zz4oVIWiKo1/e2lfDD5ILat4BRtlwnvb0T+sVCAJMiGa5abikPeFFdSvL3b\nEOn/1oTXTmIEOzVzKorBVHqlcO4kXWGrsbGGRNnaqjKDqZhhueoGIlznHvHx\nMqupYFbHhMa7ovvozw++BOU4in9PRCE1dvBx70VlPBmrTei8su5fWooGeSlk\nCx8uxaUR7p6anrRvoMlf+EOBqFy+2tz0tNA0n/TGqTc7WflEmJStCMNB0L1S\nH5QOav5Gtj3qJ3TdgSXIJ7yd+14nNPSqyZm2aDzTde3/PaeZoNfuWusHMaTI\ntShVfvp0qbBSpMucM5DOhf5KYJiZgalM/2XyIv4BZFDKk3cURjPnH9tsk5RT\neNsdN4GPi6yPUzESGvwN6W1McxIBk5EF637ca5kqDkFvtGd3ybZ7iCtj8p6o\nAu9Q\r\n=bDA1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB8q4WsE4+WVM2yhjmTScCorbHZNB2WVUPAkG+VHRJfmAiEA+ptsnxw2LQEtWe6uCSrjy2JPbJ81pERikFp/JIdkR1I="}]},"_npmUser":{"name":"balavishnuvj","email":"balavishnu.06@gmail.com"},"directories":{},"maintainers":[{"name":"balavishnuvj","email":"balavishnu.06@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/remix-seo_1.0.1_1643641261287_0.736299286650854"},"_hasShrinkwrap":false},"1.0.2":{"name":"@balavishnuvj/remix-seo","version":"1.0.2","description":"Collection of SEO utilities like sitemap, robots.txt, etc. for a Remix Application","main":"./build/index.js","private":false,"scripts":{"build":"tsc --project tsconfig.json","start":"tsc -w --project tsconfig.json","test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/balavishnuvj/remix-seo.git"},"keywords":["remix","sitemap","sitemap.xml","seo","robot.txt"],"author":{"name":"Balavishnu V J","email":"hello@balavishnuvj.com","url":"https://balavishnuvj.com"},"license":"MIT","bugs":{"url":"https://github.com/balavishnuvj/remix-seo/issues"},"homepage":"https://github.com/balavishnuvj/remix-seo#readme","devDependencies":{"@types/lodash":"^4.14.178","@types/node":"^17.0.13","typescript":"^4.5.5"},"peerDependencies":{"@remix-run/react":"^1.0.0","@remix-run/server-runtime":"^1.0.0"},"dependencies":{"lodash":"^4.17.21"},"types":"./build/index.d.ts","gitHead":"e90291ecbf4cbd694d13d5e69b4fe7442c5bba1c","_id":"@balavishnuvj/remix-seo@1.0.2","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-uSG0bc8ex0wj+RSO/hhtI/9BK7WlopYtIyrCR9A5GtwPyodNelR2ZRKg1PmyfSEndVgPIUFvpmCKT8GLZQbrYw==","shasum":"0e2a6dc82509b6801f046ad15c0d455b3294e9d2","tarball":"https://registry.npmjs.org/@balavishnuvj/remix-seo/-/remix-seo-1.0.2.tgz","fileCount":32,"unpackedSize":41889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiAU8rCRA9TVsSAnZWagAAMz8P/jGL1Qs7gJuthnOGpagp\nIp/Syml6tT886UQqiFoxboYKFEyxKCmeVw/7jR3gG2MzsvHxma5Me7U9X8l+\ngC+T5Y7fJ+XxuR5jpw3yFIMyLIpty2gGvwd8dkJut6vxJCpwePEz0J1zaF1X\ne3LiHQUgLtUStasVrVzilZFBY67MIeickIqu8g9358BpuuzALFTZ3LxIeH13\nulQPNUy4hg1AG2zpghhk0JDYddr3SpREpy303PvAboqyTVkjg5qScVCZaXg4\nGGneQsGFg0+AynOdyvhRo0srxppKxqGsyOcbODp3GA0vBGiA5AI2PsZ6g0RP\nfYyj38JIR5MkIdz3/E2SGLFfnBsWE0mO377XAtn/9fc1AIahuUjl7GKW3Y3c\njWVAT6wapOCaL9c2udWQjMkk7gjcCWjzdKZYLSJsr+3bFhq1nXIwG1H1lXlF\nrz/6z4HJuLLregJHxJUAz6512bMTOh/tRMv66rtlchbxP19aXWc87M+pylWq\nAj1BJGMnLr1Q6ZHs2BmZkRUTU3EkAYzz8GzlgAqTj05jRF8R34rT2RPREK45\nhfr6/FweF6iKI1o02t/btVrfPvTcP3WNYw5eT6VVvF0LVOqAffFXBMbFtjQU\nHglxfDOqlGPw770T44imqCIWZS1huu6QcExPXK3fhuS3pTESngL4kLYA3xF9\n+Typ\r\n=yeRu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC89ZexbApsQ1VXjwYeqPJ/XNUzIPz/bD42mVKxRGUHAAiAIZaspztHjOcLYm+QgnUzPz1tWLfgz7XUzfTWZCH+Pjg=="}]},"_npmUser":{"name":"balavishnuvj","email":"balavishnu.06@gmail.com"},"directories":{},"maintainers":[{"name":"balavishnuvj","email":"balavishnu.06@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/remix-seo_1.0.2_1644252971581_0.6424603661482806"},"_hasShrinkwrap":false}},"time":{"created":"2022-01-31T14:57:11.010Z","1.0.0":"2022-01-31T14:57:11.273Z","modified":"2022-04-04T17:32:29.038Z","1.0.1":"2022-01-31T15:01:01.449Z","1.0.2":"2022-02-07T16:56:11.742Z"},"maintainers":[{"name":"balavishnuvj","email":"balavishnu.06@gmail.com"}],"description":"Collection of SEO utilities like sitemap, robots.txt, etc. for a Remix Application","homepage":"https://github.com/balavishnuvj/remix-seo#readme","keywords":["remix","sitemap","sitemap.xml","seo","robot.txt"],"repository":{"type":"git","url":"git+https://github.com/balavishnuvj/remix-seo.git"},"author":{"name":"Balavishnu V J","email":"hello@balavishnuvj.com","url":"https://balavishnuvj.com"},"bugs":{"url":"https://github.com/balavishnuvj/remix-seo/issues"},"license":"MIT","readme":"# Remix SEO\n\nCollection of SEO utilities like sitemap, robots.txt, etc. for a [Remix](https://remix.run/)\n\n# Features\n\n- Generate Sitemap\n- Generate Robots.txt\n\n# Installation\n\nTo use it, install it from npm (or yarn):\n\n```sh\nnpm install @balavishnuvj/remix-seo\n```\n\n# Usage\n\nFor all miscellaneous routes in root like `/robots.txt`, `/sitemap.xml`. We can create a single function to handle all of them instead polluting our `routes` folder.\n\nFor that, lets create a file called `otherRootRoutes.server.ts` (file could be anything, make sure it is import only in server by ending with`.server.{ts|js}`)\n\n```ts\n// otherRootRoutes.server.ts\n\nimport { EntryContext } from \"remix\";\n\ntype Handler = (\n  request: Request,\n  remixContext: EntryContext\n) => Promise<Response | null> | null;\n\nexport const otherRootRoutes: Record<string, Handler> = {};\n\nexport const otherRootRouteHandlers: Array<Handler> = [\n  ...Object.entries(otherRootRoutes).map(([path, handler]) => {\n    return (request: Request, remixContext: EntryContext) => {\n      if (new URL(request.url).pathname !== path) return null;\n      return handler(request, remixContext);\n    };\n  }),\n];\n```\n\nand import this file in your `entry.server.tsx`\n\n```diff\nimport { renderToString } from \"react-dom/server\";\nimport { RemixServer } from \"remix\";\nimport type { EntryContext } from \"remix\";\n+import { otherRootRouteHandlers } from \"./otherRootRoutes.server\";\n\nexport default async function handleRequest(\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  remixContext: EntryContext\n) {\n+  for (const handler of otherRootRouteHandlers) {\n+    const otherRouteResponse = await handler(request, remixContext);\n+    if (otherRouteResponse) return otherRouteResponse;\n+  }\n  let markup = renderToString(\n    <RemixServer context={remixContext} url={request.url} />\n  );\n\n  responseHeaders.set(\"Content-Type\", \"text/html\");\n\n  return new Response(\"<!DOCTYPE html>\" + markup, {\n    status: responseStatusCode,\n    headers: responseHeaders,\n  });\n}\n```\n\n## Sitemap\n\nTo generate sitemap, `@balavishnuvj/remix-seo` would need context of all your routes.\n\nIf you have already created a file to handle all root routes. If not, [check above](#usage)\n\nAdd config for your sitemap\n\n```ts\nimport { EntryContext } from \"remix\";\nimport { generateSitemap } from \"@balavishnuvj/remix-seo\";\n\ntype Handler = (\n  request: Request,\n  remixContext: EntryContext\n) => Promise<Response | null> | null;\n\nexport const otherRootRoutes: Record<string, Handler> = {\n  \"/sitemap.xml\": async (request, remixContext) => {\n    return generateSitemap(request, remixContext, {\n      siteUrl: \"https://balavishnuvj.com\",\n    });\n  },\n};\n\nexport const otherRootRouteHandlers: Array<Handler> = [\n  ...Object.entries(otherRootRoutes).map(([path, handler]) => {\n    return (request: Request, remixContext: EntryContext) => {\n      if (new URL(request.url).pathname !== path) return null;\n\n      return handler(request, remixContext);\n    };\n  }),\n];\n```\n\n`generateSitemap` takes three params `request`, `EntryContext`, and `SEOOptions`.\n\n### Configuration\n\n- `SEOOptions` lets you configure the sitemap\n\n```ts\nexport type SEOOptions = {\n  siteUrl: string; // URL where the site is hosted, eg. https://balavishnuvj.com\n  headers?: HeadersInit; // Additional headers\n  /*\n    eg:  \n        headers: {\n            \"Cache-Control\": `public, max-age=${60 * 5}`,\n        },\n  */\n};\n```\n\n- To not generate sitemap for a route\n\n```ts\n// in your routes/url-that-doesnt-need-sitemap\nimport { SEOHandle } from \"@balavishnuvj/remix-seo\";\n\nexport let loader: LoaderFunction = ({ request }) => {\n  /**/\n};\n\nexport const handle: SEOHandle = {\n  getSitemapEntries: () => null,\n};\n```\n\n- To generate sitemap for dynamic routes\n\n```ts\n// routes/blog/$blogslug.tsx\n\nexport const handle: SEOHandle = {\n  getSitemapEntries: async (request) => {\n    const blogs = await db.blog.findMany();\n    return blogs.map((blog) => {\n      return { route: `/blog/${blog.slug}`, priority: 0.7 };\n    });\n  },\n};\n```\n\n## Robots\n\nYou can add this part of the root routes as did above[check above](#usage). Or else you can create a new file in your `routes` folder with `robots[.txt].ts`\n\nTo generate `robots.txt`\n\n```ts\ngenerateRobotsTxt([\n  { type: \"sitemap\", value: \"https://balavishnuvj.com/sitemap.xml\" },\n  { type: \"disallow\", value: \"/admin\" },\n]);\n```\n\n`generateRobotsTxt` takes two arguments.\n\nFirst one is array of `policies`\n\n```ts\nexport type RobotsPolicy = {\n  type: \"allow\" | \"disallow\" | \"sitemap\" | \"crawlDelay\" | \"userAgent\";\n  value: string;\n};\n```\n\nand second parameter `RobotsConfig` is for additional configuration\n\n```ts\nexport type RobotsConfig = {\n  appendOnDefaultPolicies?: boolean; // If default policies should used\n  /*\n  Default policy\n    const defaultPolicies: RobotsPolicy[] = [\n    {\n        type: \"userAgent\",\n        value: \"*\",\n    },\n    {\n        type: \"allow\",\n        value: \"/\",\n    },\n    ];\n  */\n  headers?: HeadersInit; // Additional headers\n  /*\n    eg:  \n        headers: {\n            \"Cache-Control\": `public, max-age=${60 * 5}`,\n        },\n  */\n};\n```\n\nIf you are using single function to create both `sitemap` and `robots.txt`\n\n```ts\nimport { EntryContext } from \"remix\";\nimport { generateRobotsTxt, generateSitemap } from \"@balavishnuvj/remix-seo\";\n\ntype Handler = (\n  request: Request,\n  remixContext: EntryContext\n) => Promise<Response | null> | null;\n\nexport const otherRootRoutes: Record<string, Handler> = {\n  \"/sitemap.xml\": async (request, remixContext) => {\n    return generateSitemap(request, remixContext, {\n      siteUrl: \"https://balavishnuvj.com\",\n      headers: {\n        \"Cache-Control\": `public, max-age=${60 * 5}`,\n      },\n    });\n  },\n  \"/robots.txt\": async () => {\n    return generateRobotsTxt([\n      { type: \"sitemap\", value: \"https://balavishnuvj.com/sitemap.xml\" },\n      { type: \"disallow\", value: \"/admin\" },\n    ]);\n  },\n};\n\nexport const otherRootRouteHandlers: Array<Handler> = [\n  ...Object.entries(otherRootRoutes).map(([path, handler]) => {\n    return (request: Request, remixContext: EntryContext) => {\n      if (new URL(request.url).pathname !== path) return null;\n\n      return handler(request, remixContext);\n    };\n  }),\n];\n```\n","readmeFilename":"README.md"}