Repository: expo/expo
File: packages/expo-server/src/vendor/abstract.ts (TypeScript)

Code before:
          continue;
        }

        return respondRedirect(url, request, route);
      }
    }

    if (manifest.rewrites) {
      for (const route of manifest.rewrites) {
        if (!route.namedRegex.test(url.pathname)) {
          continue;
        }

        if (route.methods && !route.methods.includes(request.method)) {
          continue;
        }

        // Replace URL and Request with rewrite target
        url = getRedirectRewriteLocation(url, request, route);
        request = new Request(url, request);
      }
    }

    // First, test static routes and loader data requests
    if (request.method === 'GET' || request.method === 'HEAD') {
      const isLoaderRequest = url.pathname.startsWith('/_expo/loaders/');
      const matchedPath = isLoaderRequest
        ? url.pathname.replace('/_expo/loaders', '').replace(/\/index$/, '/')
        : url.pathname;

      for (const route of manifest.htmlRoutes) {
        if (!route.namedRegex.test(matchedPath)) {
          continue;
        }

        // Handle loader data requests for client-side navigation
        if (isLoaderRequest) {
          if (!route.loader) {
            continue; // Route matched but has no loader
          }
          // Create a request with the actual route path so `parseParams()` works correctly
          // NOTE(@hassankhan): Relocate the request rewriting logic from here
          url.pathname = matchedPath;
          const loaderRequest = new Request(url, request);
          return createResponseFrom('api', route, await getLoaderData(loaderRequest, route));
        }

        const html = await getHtml(request, route);
        return respondHTML(html, route);
      }
    }

Review comment (performance):
This is probably good enough for now. We should maybe look into where the URL is coming from eventually instead. Technically, normalising it here means that it's already slipped through somewhere else.

Since we're normalising route names, unless we're literally accounting for `*/index` rather than `*/`, I believe this would mean that there's a missing normalisation step elsewhere? The main reason that's relevant is that it'd potentially create two separate CDN cache entries, when this is deployed

Code after:

        return respondRedirect(url, request, route);
      }
    }

    if (manifest.rewrites) {
      for (const route of manifest.rewrites) {
        if (!route.namedRegex.test(url.pathname)) {
          continue;
        }

        if (route.methods && !route.methods.includes(request.method)) {
          continue;
        }

        // Replace URL and Request with rewrite target
        url = getRedirectRewriteLocation(url, request, route);
        request = new Request(url, request);
      }
    }

    // First, test static routes and loader data requests
    if (request.method === 'GET' || request.method === 'HEAD') {
      const isLoaderRequest = url.pathname.startsWith('/_expo/loaders/');
      const matchedPath = isLoaderRequest
        ? url.pathname.replace('/_expo/loaders', '').replace(/\/index$/, '/')
        : url.pathname;

      for (const route of manifest.htmlRoutes) {
        if (!route.namedRegex.test(matchedPath)) {
          continue;
        }

        // Handle loader data requests for client-side navigation
        if (isLoaderRequest) {
          if (!route.loader) {
            continue; // Route matched but has no loader
          }
          // Create a request with the actual route path so `parseParams()` works correctly
          // NOTE(@hassankhan): Relocate the request rewriting logic from here
          url.pathname = matchedPath;
          const loaderRequest = new Request(url, request);
          return createResponseFrom('api', route, await getLoaderData(loaderRequest, route));
        }

        const html = await getHtml(request, route);
        return respondHTML(html, route);
      }
    }

    // Next, test API routes