Repository: remix-run/react-router
File: packages/react-router-dev/vite/plugin.ts (TypeScript)

Code before:
  viteConfig.logger.info(`Prerender: Generated ${colors.bold(outfile)}`);
}

async function prerenderManifest(
  build: ServerBuild,
  clientBuildDirectory: string,
  reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
  viteConfig: Vite.ResolvedConfig
) {
  let normalizedPath = `${reactRouterConfig.basename}/__manifest`.replace(
    /\/\/+/g,
    "/"
  );
  let outdir = path.relative(process.cwd(), clientBuildDirectory);
  let outfile = path.join(outdir, ...normalizedPath.split("/"));
  await fse.ensureDir(path.dirname(outfile));
  let manifestData = JSON.stringify({
    notFoundPaths: [],
    patches: build.assets.routes,
  });
  await fse.outputFile(outfile, manifestData);
  viteConfig.logger.info(`Prerender: Generated ${colors.bold(outfile)}`);
}

function validatePrerenderedResponse(
  response: Response,
  html: string,
  prefix: string,
  path: string
) {
  if (response.status !== 200) {
    throw new Error(
      `${prefix}: Received a ${response.status} status code from ` +
        `\`entry.server.tsx\` while prerendering the \`${path}\` ` +
        `path.\n${html}`
    );
  }
}

function validatePrerenderedHtml(html: string, prefix: string) {
  if (
    !html.includes("window.__remixContext =") ||
    !html.includes("window.__remixRouteModules =")
  ) {
    throw new Error(
      `${prefix}: Did you forget to include <Scripts/> in your root route? ` +
        "Your pre-rendered HTML files cannot hydrate without `<Scripts />`."
    );
  }
}


Review comment (suggestion):
This was accidentally a locally scoped function inside the parent `handlePreRender` function.  No changes were made - it just moves out to be a module level function

Code after:

  // Then recursively add any new path defined by the tree
  function recurse(subtree: typeof routes, prefix = "") {
    for (let route of subtree) {
      let newPath = [prefix, route.path].join("/").replace(/\/\/+/g, "/");
      if (route.path) {
        let segments = route.path.split("/");
        if (segments.some((s) => s.startsWith(":") || s === "*")) {
          paramRoutes.push(route.path);
        } else {
          paths.push(newPath);
        }
      }
      if (route.children) {
        recurse(route.children, newPath);
      }
    }
  }
  recurse(routes);

  if (isBooleanUsage && paramRoutes) {
    viteConfig.logger.warn(
      "The following paths were not prerendered because Dynamic Param and Splat " +
        "routes cannot be prerendered when using `prerender:true`. You may want to " +
        "consider using the `prerender()` API if you wish to prerender slug and " +
        "splat routes."
    );
  }

  // Clean double slashes and remove trailing slashes
  return paths.map((p) => p.replace(/\/\/+/g, "/").replace(/(.+)\/$/, "$1"));
}

async function prerenderData(
  handler: RequestHandler,
  prerenderPath: string,
  clientBuildDirectory: string,
  reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
  viteConfig: Vite.ResolvedConfig,
  requestInit: RequestInit
) {
  let normalizedPath = `${reactRouterConfig.basename}${
    prerenderPath === "/"
      ? "/_root.data"
      : `${prerenderPath.replace(/\/$/, "")}.data`
  }`.replace(/\/\/+/g, "/");
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
  let response = await handler(request);
  let data = await response.text();

  validatePrerenderedResponse(response, data, "Prerender", normalizedPath);