fedeya / remix-sitemap

Sitemap generator for Remix applications
https://npmjs.com/remix-sitemap
MIT License
95 stars 5 forks source link

'No route matches URL' console errors #55

Open lewsmith opened 12 months ago

lewsmith commented 12 months ago

I've got my sitemaps setup and generating output okay - works like a charm.

However in the console I'm seeing errors about route matching. Example for a /sitemap.txt request, which displays the sitemap fine, but shows this error for every request.

GET /sitemap.xml 200 773 - 1.659 ms
Error: No route matches URL "/robots.txt"
    at getInternalRouterError (./node_modules/.pnpm/@remix-run+router@1.9.0/node_modules/@remix-run/router/router.ts:4144:5)
    at Object.query (./node_modules/.pnpm/@remix-run+router@1.9.0/node_modules/@remix-run/router/router.ts:2628:19)
    at handleDocumentRequestRR (./node_modules/.pnpm/@remix-run+server-runtime@2.0.0_typescript@5.2.2/node_modules/@remix-run/server-runtime/dist/server.js:138:35)
    at requestHandler (./node_modules/.pnpm/@remix-run+server-runtime@2.0.0_typescript@5.2.2/node_modules/@remix-run/server-runtime/dist/server.js:63:24)
    at ./node_modules/.pnpm/@remix-run+express@2.0.0_express@4.18.2_typescript@5.2.2/node_modules/@remix-run/express/dist/server.js:41:28

I think I've set it up exactly as required in my entry.server.tsx:

import { PassThrough } from 'node:stream';

import {
  EntryContext,
  createReadableStreamFromReadable,
} from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import isbot from 'isbot';
import { renderToPipeableStream } from 'react-dom/server';
import { createSitemapGenerator } from 'remix-sitemap';
import { configMeta } from './configs';

const ABORT_DELAY = 5_000;

const { isSitemapUrl, sitemap } = createSitemapGenerator({
  siteUrl: configMeta?.url,
  generateRobotsTxt: true,
});

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) {
  if (isSitemapUrl(request)) {
    return await sitemap(request, remixContext);
  }

  return isbot(request.headers.get('user-agent'))
    ? handleBotRequest(
        request,
        responseStatusCode,
        responseHeaders,
        remixContext,
      )
    : handleBrowserRequest(
        request,
        responseStatusCode,
        responseHeaders,
        remixContext,
      );
}

function handleBotRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) {
  return new Promise((resolve, reject) => {
    let shellRendered = false;
    const { pipe, abort } = renderToPipeableStream(
      <RemixServer
        context={remixContext}
        url={request.url}
        abortDelay={ABORT_DELAY}
      />,
      {
        onAllReady() {
          shellRendered = true;
          const body = new PassThrough();

          responseHeaders.set('Content-Type', 'text/html');

          resolve(
            new Response(createReadableStreamFromReadable(body), {
              headers: responseHeaders,
              status: responseStatusCode,
            }),
          );

          pipe(body);
        },
        onShellError(error: unknown) {
          reject(error);
        },
        onError(error: unknown) {
          responseStatusCode = 500;
          if (shellRendered) {
            console.error(error);
          }
        },
      },
    );

    setTimeout(abort, ABORT_DELAY);
  });
}

function handleBrowserRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) {
  return new Promise((resolve, reject) => {
    let shellRendered = false;
    const { pipe, abort } = renderToPipeableStream(
      <RemixServer
        context={remixContext}
        url={request.url}
        abortDelay={ABORT_DELAY}
      />,
      {
        onShellReady() {
          shellRendered = true;
          const body = new PassThrough();

          responseHeaders.set('Content-Type', 'text/html');

          resolve(
            new Response(createReadableStreamFromReadable(body), {
              headers: responseHeaders,
              status: responseStatusCode,
            }),
          );

          pipe(body);
        },
        onShellError(error: unknown) {
          reject(error);
        },
        onError(error: unknown) {
          responseStatusCode = 500;
          if (shellRendered) {
            console.error(error);
          }
        },
      },
    );

    setTimeout(abort, ABORT_DELAY);
  });
}

One way I managed to stop the error is by creating the route routes/sitemap[.]xml.tsx and just return null, but that seems a bit hacky.

Have I done something wrong? Does anyone else see this?

Expected behavior To show either the sitemap or robots file, without the console displaying the errors.

Environment (please complete the following information):

lewsmith commented 12 months ago

Another way to avoid these errors is to define some routes in the remix config, but I'm not sure if this is any better than creating the two null routes TBH.

E.g.

// remix.confg.js
export default {
   ...
   routes: async (defineRoutes) => { 
    return defineRoutes((route) => {
      route("/sitemap.xml", "routes/_null.tsx", {id: 'routes/sitemap.xml'}),
      route("/robots.txt", "routes/_null.tsx", {id: 'routes/robots.txt'})
    })
  }
};
// routes/_null.tsx
export default function NullRoute() {
  return null;
}
justinhandley commented 11 months ago

I'm seeing the same thing - the sitemap renders fine but I get this:

Error: No route matches URL "/sitemap.xml"
    at getInternalRouterError (/Users/justinhandley/IdeaProjects/muzebook/node_modules/.pnpm/@remix-run+router@1.9.0/node_modules/@remix-run/router/router.ts:4144:5)
    at Object.query (/Users/justinhandley/IdeaProjects/muzebook/node_modules/.pnpm/@remix-run+router@1.9.0/node_modules/@remix-run/router/router.ts:2628:19)
    at handleDocumentRequestRR (/Users/justinhandley/IdeaProjects/muzebook/node_modules/.pnpm/@remix-run+server-runtime@2.0.1_typescript@5.1.6/node_modules/@remix-run/server-runtime/dist/server.js:138:35)
    at requestHandler (/Users/justinhandley/IdeaProjects/muzebook/node_modules/.pnpm/@remix-run+server-runtime@2.0.1_typescript@5.1.6/node_modules/@remix-run/server-runtime/dist/server.js:63:24)
    at /Users/justinhandley/IdeaProjects/muzebook/node_modules/.pnpm/@remix-run+express@2.0.1_express@4.18.2_typescript@5.1.6/node_modules/@remix-run/express/dist/server.js:41:28

GET /sitemap.xml 200 767 - 53.260 ms
fedeya commented 11 months ago

It seems this is a remix v2 bug. If this doesn't improve in future versions of the remix, it may be necessary to think about somewhere else to put the sitemap response.

fedeya commented 10 months ago

Hi @lewsmith @justinhandley to fix this i released a new version (3.1.0) with a new api to just generate the sitemap and robots in each route, i marked it as experimental because i didn't test it on all plataforms.

Here you can see a usage guide https://github.com/fedeya/remix-sitemap/releases/tag/v3.1.0