diff --git a/.changeset/lazy-dots-sip.md b/.changeset/lazy-dots-sip.md
new file mode 100644
index 00000000000..d812bcc3d57
--- /dev/null
+++ b/.changeset/lazy-dots-sip.md
@@ -0,0 +1,6 @@
+---
+"remix": patch
+"@remix-run/dev": patch
+---
+
+add warning when `future.v2_routeConvention` is not enabled
diff --git a/integration/flat-routes-test.ts b/integration/flat-routes-test.ts
index 9254ca16ee5..49a4937f555 100644
--- a/integration/flat-routes-test.ts
+++ b/integration/flat-routes-test.ts
@@ -5,6 +5,7 @@ import { PlaywrightFixture } from "./helpers/playwright-fixture";
import type { Fixture, AppFixture } from "./helpers/create-fixture";
import { createFixtureProject } from "./helpers/create-fixture";
import { createAppFixture, createFixture, js } from "./helpers/create-fixture";
+import { flatRoutesWarning } from "../packages/remix-dev/config";
let fixture: Fixture;
let appFixture: AppFixture;
@@ -147,6 +148,51 @@ test.describe("flat routes", () => {
}
});
+test.describe("warns when v1 routesConvention is used", () => {
+ let buildStdio = new PassThrough();
+ let buildOutput: string;
+
+ let originalConsoleLog = console.log;
+ let originalConsoleWarn = console.warn;
+ let originalConsoleError = console.error;
+
+ test.beforeAll(async () => {
+ console.log = () => {};
+ console.warn = () => {};
+ console.error = () => {};
+ await createFixtureProject({
+ buildStdio,
+ files: {
+ "routes/index.tsx": js`
+ export default function () {
+ return
routes/index
;
+ }
+ `,
+ },
+ });
+
+ let chunks: Buffer[] = [];
+ buildOutput = await new Promise((resolve, reject) => {
+ buildStdio.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
+ buildStdio.on("error", (err) => reject(err));
+ buildStdio.on("end", () =>
+ resolve(Buffer.concat(chunks).toString("utf8"))
+ );
+ });
+ });
+
+ test.afterAll(() => {
+ console.log = originalConsoleLog;
+ console.warn = originalConsoleWarn;
+ console.error = originalConsoleError;
+ });
+
+ test("warns about conflicting routes", () => {
+ console.log(buildOutput);
+ expect(buildOutput).toContain(flatRoutesWarning);
+ });
+});
+
test.describe("emits warnings for route conflicts", async () => {
let buildStdio = new PassThrough();
let buildOutput: string;
diff --git a/integration/hmr-test.ts b/integration/hmr-test.ts
index 1381038e0e4..63a88293661 100644
--- a/integration/hmr-test.ts
+++ b/integration/hmr-test.ts
@@ -14,6 +14,7 @@ let fixture = (options: { port: number; appServerPort: number }) => ({
appServerPort: options.appServerPort,
},
unstable_tailwind: true,
+ v2_routeConvention: true,
},
files: {
"package.json": `
@@ -124,7 +125,7 @@ let fixture = (options: { port: number; appServerPort: number }) => ({
);
}
`,
- "app/routes/index.tsx": `
+ "app/routes/_index.tsx": `
import { useLoaderData } from "@remix-run/react";
export default function Index() {
const t = useLoaderData();
@@ -235,7 +236,7 @@ test("HMR", async ({ page }) => {
await counter.click();
await page.waitForSelector(`#root-counter:has-text("inc 1")`);
- let indexPath = path.join(projectDir, "app", "routes", "index.tsx");
+ let indexPath = path.join(projectDir, "app", "routes", "_index.tsx");
let originalIndex = fs.readFileSync(indexPath, "utf8");
let counterPath = path.join(projectDir, "app", "components", "counter.tsx");
let originalCounter = fs.readFileSync(counterPath, "utf8");
diff --git a/integration/tsconfig.json b/integration/tsconfig.json
index b8fd6c97ac7..d2215944147 100644
--- a/integration/tsconfig.json
+++ b/integration/tsconfig.json
@@ -17,6 +17,7 @@
"rootDir": "."
},
"references": [
+ { "path": "../packages/remix-dev" },
{ "path": "../packages/remix-express" },
{ "path": "../packages/remix-react" },
{ "path": "../packages/remix-server-runtime" }
diff --git a/packages/remix-dev/__tests__/create-test.ts b/packages/remix-dev/__tests__/create-test.ts
index 4a0fbed9768..4508ac91907 100644
--- a/packages/remix-dev/__tests__/create-test.ts
+++ b/packages/remix-dev/__tests__/create-test.ts
@@ -8,6 +8,7 @@ import stripAnsi from "strip-ansi";
import { run } from "../cli/run";
import { server } from "./msw";
+import { flatRoutesWarning } from "../config";
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterAll(() => server.close());
@@ -347,7 +348,9 @@ describe("the create command", () => {
"--no-typescript",
]);
expect(output.trim()).toBe(
- getOptOutOfInstallMessage() +
+ flatRoutesWarning +
+ "\n\n" +
+ getOptOutOfInstallMessage() +
"\n\n" +
getSuccessMessage(path.join("", "template-to-js"))
);
diff --git a/packages/remix-dev/config.ts b/packages/remix-dev/config.ts
index 4c6f161eb86..8686777438a 100644
--- a/packages/remix-dev/config.ts
+++ b/packages/remix-dev/config.ts
@@ -560,9 +560,14 @@ export async function readConfig(
root: { path: "", id: "root", file: rootRouteFile },
};
- let routesConvention = appConfig.future?.v2_routeConvention
- ? flatRoutes
- : defineConventionalRoutes;
+ let routesConvention: typeof flatRoutes;
+
+ if (appConfig.future?.v2_routeConvention) {
+ routesConvention = flatRoutes;
+ } else {
+ warnOnce(flatRoutesWarning, "v2_routeConvention");
+ routesConvention = defineConventionalRoutes;
+ }
if (fse.existsSync(path.resolve(appDirectory, "routes"))) {
let conventionalRoutes = routesConvention(
@@ -726,4 +731,6 @@ let listFormat = new Intl.ListFormat("en", {
type: "conjunction",
});
-export let serverBuildTargetWarning = `The "serverBuildTarget" config option is deprecated. Use a combination of "publicPath", "serverBuildPath", "serverConditions", "serverDependenciesToBundle", "serverMainFields", "serverMinify", "serverModuleFormat" and/or "serverPlatform" instead.`;
+export let serverBuildTargetWarning = `⚠️ DEPRECATED: The "serverBuildTarget" config option is deprecated. Use a combination of "publicPath", "serverBuildPath", "serverConditions", "serverDependenciesToBundle", "serverMainFields", "serverMinify", "serverModuleFormat" and/or "serverPlatform" instead.`;
+
+export let flatRoutesWarning = `⚠️ DEPRECATED: The old nested folders route convention has been deprecated in favor of "flat routes". Please enable the new routing convention via the \`future.v2_routeConvention\` flag in your \`remix.config.js\` file. For more information, please see https://remix.run/docs/en/main/file-conventions/route-files-v2.`;