Repository: remix-run/remix
Pull request #6222 (2023-04-27 19:51:29)

Revert "Fix splat route index matching
Reverts PR #6130 (issue #5841)

Before #6130, Remix was creating a route tree as follows:

```jsx
<Route path="" element={<Root />}>
  { /* routes/ directory */}
</Route>
```

#6130 removed `path=""` from the root route:

```jsx
<Route element={<Root />}>
  { /* routes/ directory */}
</Route>
```

This allowed a sole `routes/$.jsx` to match the `/` route because root is now a _true_ pathless route.  

However, it breaks the previous Remix behavior that that `root.jsx` alone no longer matches `/`, so while it's not a super-communicated feature, you could no longer create remix apps with simple `root.jsx` and no `routes/` directory.

changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: 82a7e79d0e7eb9752426ac24b562278988a9a76b

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 18 packages</summary>

  | Name                          | Type  |
| ----------------------------- | ----- |
| @remix-run/dev                | Patch |
| create-remix                  | Patch |
| @remix-run/css-bundle         | Patch |
| remix                         | Patch |
| @remix-run/architect          | Patch |
| @remix-run/cloudflare         | Patch |
| @remix-run/cloudflare-pages   | Patch |
| @remix-run/cloudflare-workers | Patch |
| @remix-run/deno               | Patch |
| @remix-run/eslint-config      | Patch |
| @remix-run/express            | Patch |
| @remix-run/netlify            | Patch |
| @remix-run/node               | Patch |
| @remix-run/react              | Patch |
| @remix-run/serve              | Patch |
| @remix-run/server-runtime     | Patch |
| @remix-run/testing            | Patch |
| @remix-run/vercel             | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/remix-run/remix/new/brophdawg11/revert-splat?filename=.changeset/four-roses-arrive.md&value=---%0A%22remix%22%3A%20patch%0A%22%40remix-run%2Fdev%22%3A%20patch%0A---%0A%0ARevert%20%22Fix%20splat%20route%20index%20matching%0A)



Patch:
diff --git a/.changeset/splat-index-matching-revert.md b/.changeset/splat-index-matching-revert.md
new file mode 100644
index 000000000..7e7f87369
--- /dev/null
+++ b/.changeset/splat-index-matching-revert.md
@@ -0,0 +1,5 @@
+---
+"@remix-run/dev": patch
+---
+
+Revert "Fix a bug in route matching that was preventing a single splat (`$.jsx`) route from matching a root `/` path"
diff --git a/.changeset/splat-index-matching.md b/.changeset/splat-index-matching.md
index 3cca3d0c2..393adb5bd 100644
--- a/.changeset/splat-index-matching.md
+++ b/.changeset/splat-index-matching.md
@@ -2,4 +2,4 @@
 "@remix-run/dev": patch
 ---
 
-Fix a bug in route matching that wass preventing a single splat (`$.jsx`) route from matching a root `/` path
+Fix a bug in route matching that was preventing a single splat (`$.jsx`) route from matching a root `/` path
diff --git a/docs/tutorials/jokes.md b/docs/tutorials/jokes.md
index eba6a7f3e..c4e4bae5f 100644
--- a/docs/tutorials/jokes.md
+++ b/docs/tutorials/jokes.md
@@ -6427,7 +6427,7 @@ function validatePassword(password: string) {
 }
 
 function validateUrl(url: string) {
-  let urls = ["/jokes", "/", "https://remix.run"];
+  const urls = ["/jokes", "/", "https://remix.run"];
   if (urls.includes(url)) {
     return url;
   }
diff --git a/packages/remix-dev/CHANGELOG.md b/packages/remix-dev/CHANGELOG.md
index 505d9ea91..93865f237 100644
--- a/packages/remix-dev/CHANGELOG.md
+++ b/packages/remix-dev/CHANGELOG.md
@@ -136,11 +136,11 @@
   Enable `unstable_dev` in `remix.config.js`:
 
   ```js
-  {
+  module.exports = {
     future: {
-      "unstable_dev": true
-    }
-  }
+      unstable_dev: true,
+    },
+  };
   ```
 
   ## Remix App Server
@@ -182,12 +182,12 @@
   import { devReady } from "@remix-run/node";
 
   // Path to Remix's server build directory ('build/' by default)
-  let BUILD_DIR = path.join(process.cwd(), "build");
+  const BUILD_DIR = path.join(process.cwd(), "build");
 
   // <code setting up your express server>
 
   app.listen(3000, () => {
-    let build = require(BUILD_DIR);
+    const build = require(BUILD_DIR);
     console.log("Ready: http://localhost:" + port);
 
     // in development, call `devReady` _after_ your server is up and running
@@ -211,7 +211,7 @@
   - You want to handle server updates yourself (e.g. via require cache purging)
 
   ```js
-  {
+  module.exports = {
     future: {
       unstable_dev: {
         // Command to run your app server
@@ -227,9 +227,9 @@
         // Whether the app server should be restarted when app is rebuilt
         // See `Advanced > restart` for more
         restart: false, // default: `true`
-      }
-    }
-  }
+      },
+    },
+  };
   ```
 
   You can also configure via flags. For example:
diff --git a/packages/remix-dev/config.ts b/packages/remix-dev/config.ts
index 5133fe6d5..0e3a5e2f3 100644
--- a/packages/remix-dev/config.ts
+++ b/packages/remix-dev/config.ts
@@ -679,7 +679,7 @@ export async function readConfig(
   }
 
   let routes: RouteManifest = {
-    root: { id: "root", file: rootRouteFile },
+    root: { path: "", id: "root", file: rootRouteFile },
   };
 
   let routesConvention: typeof flatRoutes;
diff --git a/packages/remix-server-runtime/CHANGELOG.md b/packages/remix-server-runtime/CHANGELOG.md
index 05ba183bb..d9b0d29e0 100644
--- a/packages/remix-server-runtime/CHANGELOG.md
+++ b/packages/remix-server-runtime/CHANGELOG.md
@@ -124,11 +124,11 @@
   Enable `unstable_dev` in `remix.config.js`:
 
   ```js
-  {
+  module.exports = {
     future: {
-      "unstable_dev": true
-    }
-  }
+      unstable_dev: true,
+    },
+  };
   ```
 
   ## Remix App Server
@@ -170,12 +170,12 @@
   import { devReady } from "@remix-run/node";
 
   // Path to Remix's server build directory ('build/' by default)
-  let BUILD_DIR = path.join(process.cwd(), "build");
+  const BUILD_DIR = path.join(process.cwd(), "build");
 
   // <code setting up your express server>
 
   app.listen(3000, () => {
-    let build = require(BUILD_DIR);
+    const build = require(BUILD_DIR);
     console.log("Ready: http://localhost:" + port);
 
     // in development, call `devReady` _after_ your server is up and running
@@ -199,7 +199,7 @@
   - You want to handle server updates yourself (e.g. via require cache purging)
 
   ```js
-  {
+  module.exports = {
     future: {
       unstable_dev: {
         // Command to run your app server
@@ -215,9 +215,9 @@
         // Whether the app server should be restarted when app is rebuilt
         // See `Advanced > restart` for more
         restart: false, // default: `true`
-      }
-    }
-  }
+      },
+    },
+  };
   ```
 
   You can also configure via flags. For example:


Test patch:
diff --git a/integration/revalidate-test.ts b/integration/revalidate-test.ts
index b5f1721d0..00ed60ca6 100644
--- a/integration/revalidate-test.ts
+++ b/integration/revalidate-test.ts
@@ -44,11 +44,6 @@ test.describe("Revalidation", () => {
             }
           `,
 
-          "app/routes/_index.jsx": js`
-            export default function Component() {
-              return <h1>Index</h1>;
-            }
-          `,
           "app/routes/parent.jsx": js`
             import { json } from "@remix-run/node";
             import { Outlet, useLoaderData } from "@remix-run/react";
diff --git a/integration/root-route-test.ts b/integration/root-route-test.ts
new file mode 100644
index 000000000..c103b74ac
--- /dev/null
+++ b/integration/root-route-test.ts
@@ -0,0 +1,41 @@
+import { test, expect } from "@playwright/test";
+
+import { createAppFixture, createFixture, js } from "./helpers/create-fixture";
+import type { Fixture, AppFixture } from "./helpers/create-fixture";
+import { PlaywrightFixture } from "./helpers/playwright-fixture";
+
+test.describe("root route", () => {
+  let fixture: Fixture;
+  let appFixture: AppFixture;
+
+  test.beforeAll(async () => {
+    fixture = await createFixture({
+      files: {
+        "app/root.jsx": js`
+          export default function Root() {
+            return (
+              <html>
+                <body>
+                  <h1>Hello Root!</h1>
+                </body>
+              </html>
+            );
+          }
+        `,
+      },
+    });
+
+    appFixture = await createAppFixture(fixture);
+  });
+
+  test.afterAll(() => {
+    appFixture.close();
+  });
+
+  test("matches the sole root route on /", async ({ page }) => {
+    let app = new PlaywrightFixture(appFixture, page);
+    await app.goto("/");
+    await page.waitForSelector("h1");
+    expect(await app.getHtml("h1")).toMatch("Hello Root!");
+  });
+});
diff --git a/integration/splat-routes-test.ts b/integration/splat-routes-test.ts
index ed8671545..0a15ae5e6 100644
--- a/integration/splat-routes-test.ts
+++ b/integration/splat-routes-test.ts
@@ -3,9 +3,9 @@ import { test, expect } from "@playwright/test";
 import { createFixture, js } from "./helpers/create-fixture";
 import type { Fixture } from "./helpers/create-fixture";
 
-let fixture: Fixture;
-
 test.describe("rendering", () => {
+  let fixture: Fixture;
+
   let ROOT_$ = "FLAT";
   let ROOT_INDEX = "ROOT_INDEX";
   let FLAT_$ = "FLAT";
@@ -128,105 +128,3 @@ test.describe("rendering", () => {
     expect(await res.text()).toMatch(PARENTLESS_$);
   });
 });
-
-test.describe("root splat route without index", () => {
-  test("matches routes correctly (v1)", async ({ page }) => {
-    fixture = await createFixture({
-      future: { v2_routeConvention: false },
-      files: {
-        "app/routes/$.jsx": js`
-          export default function Component() {
-            return <h1>Hello Splat</h1>
-          }
-        `,
-      },
-    });
-
-    let res = await fixture.requestDocument("/");
-    expect(await res.text()).toMatch("Hello Splat");
-
-    res = await fixture.requestDocument("/splat");
-    expect(await res.text()).toMatch("Hello Splat");
-
-    res = await fixture.requestDocument("/splat/deep/path");
-    expect(await res.text()).toMatch("Hello Splat");
-  });
-
-  test("matches routes correctly (v2)", async ({ page }) => {
-    fixture = await createFixture({
-      future: { v2_routeConvention: true },
-      files: {
-        "app/routes/$.jsx": js`
-          export default function Component() {
-            return <h1>Hello Splat</h1>
-          }
-        `,
-      },
-    });
-
-    let res = await fixture.requestDocument("/");
-    expect(await res.text()).toMatch("Hello Splat");
-
-    res = await fixture.requestDocument("/splat");
-    expect(await res.text()).toMatch("Hello Splat");
-
-    res = await fixture.requestDocument("/splat/deep/path");
-    expect(await res.text()).toMatch("Hello Splat");
-  });
-});
-
-test.describe("root splat route with index", () => {
-  test("matches routes correctly (v1)", async ({ page }) => {
-    fixture = await createFixture({
-      future: { v2_routeConvention: false },
-      files: {
-        "app/routes/index.jsx": js`
-          export default function Component() {
-            return <h1>Hello Index</h1>
-          }
-        `,
-        "app/routes/$.jsx": js`
-          export default function Component() {
-            return <h1>Hello Splat</h1>
-          }
-        `,
-      },
-    });
-
-    let res = await fixture.requestDocument("/");
-    expect(await res.text()).toMatch("Hello Index");
-
-    res = await fixture.requestDocument("/splat");
-    expect(await res.text()).toMatch("Hello Splat");
-
-    res = await fixture.requestDocument("/splat/deep/path");
-    expect(await res.text()).toMatch("Hello Splat");
-  });
-
-  test("matches routes correctly (v2)", async ({ page }) => {
-    fixture = await createFixture({
-      future: { v2_routeConvention: true },
-      files: {
-        "app/routes/_index.jsx": js`
-          export default function Component() {
-            return <h1>Hello Index</h1>
-          }
-        `,
-        "app/routes/$.jsx": js`
-          export default function Component() {
-            return <h1>Hello Splat</h1>
-          }
-        `,
-      },
-    });
-
-    let res = await fixture.requestDocument("/");
-    expect(await res.text()).toMatch("Hello Index");
-
-    res = await fixture.requestDocument("/splat");
-    expect(await res.text()).toMatch("Hello Splat");
-
-    res = await fixture.requestDocument("/splat/deep/path");
-    expect(await res.text()).toMatch("Hello Splat");
-  });
-});
diff --git a/packages/remix-dev/__tests__/readConfig-test.ts b/packages/remix-dev/__tests__/readConfig-test.ts
index 8298b81e5..e1f795537 100644
--- a/packages/remix-dev/__tests__/readConfig-test.ts
+++ b/packages/remix-dev/__tests__/readConfig-test.ts
@@ -68,6 +68,7 @@ describe("readConfig", () => {
           "root": Object {
             "file": "root.tsx",
             "id": "root",
+            "path": "",
           },
         },
         "serverBuildPath": Any<String>,