diff --git a/README.md b/README.md
index 34a4e3f..f67b015 100644
--- a/README.md
+++ b/README.md
@@ -100,10 +100,101 @@ The `RemixDevTools` component accepts the following props:
- `defaultOpen`: Whether to open the Remix Development Tools by default. Defaults to `false`.
- `position`: The position of the Remix Development Tools trigger. Defaults to `bottom-right`.
- `requireUrlFlag`: Requires rdt=true to be present in the URL search to open the Remix Development Tools. Defaults to `false`.
-- `showRouteBoundaries`: Allows you to see each Outlet and route boundaries by coloring the background. Defaults to `false`.
+- [**DEPRECATED**] `showRouteBoundaries`:This flag has been deprecated in favor of adding route boundaries. Please see the section below for more information.
- `hideUntilHover`: Allows you to hide the trigger until you hover over it. Defaults to `false`.
- `additionalTabs`: Allows you to provide additional tabs to the Remix Development Tools. Defaults to `[]`.
+## Adding route boundaries
+
+The `showErrorBoundaries` flag has been deprecated in favor of this method. Please use it instead.
+
+In order to add Route boundaries to your project you need to do the following two things:
+
+1. Modify your `entry.server.ts` to add the following code:
+
+```diff
+function handleBrowserRequest(
+ request: Request,
+ responseStatusCode: number,
+ responseHeaders: Headers,
+ remixContext: EntryContext
+) {
+- return new Promise((resolve, reject) => {
++ return new Promise(async (resolve, reject) => {
+ let shellRendered = false;
++ const context = process.env.NODE_ENV === "development" ? await import("remix-development-tools").then(({ initRouteBoundariesServer }) => initRouteBoundariesServer(remixContext)) : remixContext;
+ const { pipe, abort } = renderToPipeableStream(
+ ,
+ {
+ onShellReady() {
+ shellRendered = true;
+ const body = new PassThrough();
+
+ responseHeaders.set("Content-Type", "text/html");
+
+ resolve(
+ new Response(body, {
+ headers: responseHeaders,
+ status: responseStatusCode,
+ })
+ );
+
+ pipe(body);
+ },
+ onShellError(error: unknown) {
+ reject(error);
+ },
+ onError(error: unknown) {
+ responseStatusCode = 500;
+ // Log streaming rendering errors from inside the shell. Don't log
+ // errors encountered during initial shell rendering since they'll
+ // reject and get logged in handleDocumentRequest.
+ if (shellRendered) {
+ console.error(error);
+ }
+ },
+ }
+ );
+
+ setTimeout(abort, ABORT_DELAY);
+ });
+}
+```
+
+2. Modify your `entry.client.tsx` to add the following code:
+
+```diff
++ if(process.env.NODE_ENV === "development") {
++ import("remix-development-tools").then(({ initRouteBoundariesClient }) => {
++ initRouteBoundariesClient();
++ startTransition(() => {
++ hydrateRoot(
++ document,
++
++
++
++ );
++ });
++
++ });
++ } else {
+ startTransition(() => {
+ hydrateRoot(
+ document,
+
+
+
+ );
+ });
++ }
+```
+3. You are good to go. Now you can see the route boundaries in your project when you hover each route.
+
## Plugins
Writing plugins for Remix Development Tools is easy. You can write a plugin that adds a new tab to the Remix Development Tools in the following way:
diff --git a/package-lock.json b/package-lock.json
index 0abff9c..e6292ad 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "remix-development-tools",
- "version": "1.2.2",
+ "version": "1.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "remix-development-tools",
- "version": "1.2.2",
+ "version": "1.4.0",
"license": "MIT",
"workspaces": [
".",
diff --git a/package.json b/package.json
index 39f9596..485babb 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "remix-development-tools",
"description": "Remix development tools.",
"author": "Alem Tuzlak",
- "version": "1.3.0",
+ "version": "1.4.0",
"license": "MIT",
"keywords": [
"remix",
diff --git a/src/RemixDevTools/RemixDevTools.tsx b/src/RemixDevTools/RemixDevTools.tsx
index 46ca410..07eb757 100644
--- a/src/RemixDevTools/RemixDevTools.tsx
+++ b/src/RemixDevTools/RemixDevTools.tsx
@@ -5,7 +5,6 @@ import { useTimelineHandler } from "./hooks/useTimelineHandler";
import { useRDTContext } from "./context/useRDTContext";
import { isDev } from "./utils/isDev";
import { useLocation } from "@remix-run/react";
-import { useOutletAugment } from "./hooks/useOutletAugment";
import { Trigger } from "./components/Trigger";
import { MainPanel } from "./layout/MainPanel";
import { Tabs } from "./layout/Tabs";
@@ -15,7 +14,6 @@ interface Props extends RemixDevToolsProps {
defaultOpen: boolean;
position: Exclude;
hideUntilHover: boolean;
- showRouteBoundaries: boolean;
}
const RemixDevTools = ({
@@ -23,9 +21,7 @@ const RemixDevTools = ({
position,
additionalTabs,
hideUntilHover,
- showRouteBoundaries,
}: Props) => {
- useOutletAugment(showRouteBoundaries);
useTimelineHandler();
const { persistOpen } = useRDTContext();
const [isOpen, setIsOpen] = useState(defaultOpen || persistOpen);
@@ -49,7 +45,6 @@ const RemixDevTools = ({
);
};
-
let hydrating = true;
function useHydrated() {
@@ -78,8 +73,6 @@ export interface RemixDevToolsProps {
| "top-left"
| "middle-right"
| "middle-left";
- // Show route boundaries when you hover over a route in active page tab
- showRouteBoundaries?: boolean;
// Additional tabs to add to the dev tools
additionalTabs?: Tab[];
// Whether the dev tools trigger should hide until hovered
@@ -90,7 +83,6 @@ const RDTWithContext = ({
port = 3003,
defaultOpen = false,
requireUrlFlag,
- showRouteBoundaries = false,
position = "bottom-right",
hideUntilHover = false,
additionalTabs,
@@ -101,14 +93,14 @@ const RDTWithContext = ({
if (!hydrated || !isDevelopment) return null;
if (requireUrlFlag && !url.includes("rdt=true")) return null;
+
return (
-
+
);
diff --git a/src/RemixDevTools/context/RDTContext.tsx b/src/RemixDevTools/context/RDTContext.tsx
index 94d7f9f..f7e5ac6 100644
--- a/src/RemixDevTools/context/RDTContext.tsx
+++ b/src/RemixDevTools/context/RDTContext.tsx
@@ -22,18 +22,13 @@ interface ContextProps {
export const REMIX_DEV_TOOLS = "remixDevTools";
-export const RDTContextProvider = ({
- children,
- port,
- showRouteBoundaries,
-}: ContextProps) => {
+export const RDTContextProvider = ({ children, port }: ContextProps) => {
const existingState = sessionStorage.getItem(REMIX_DEV_TOOLS);
const settings = localStorage.getItem(REMIX_DEV_TOOLS);
const [state, dispatch] = useReducer(rdtReducer, {
...initialState,
...(existingState ? JSON.parse(existingState) : {}),
- showRouteBoundaries,
settings: settings
? { ...initialState.settings, ...JSON.parse(settings), port }
: { ...initialState.settings, port },
diff --git a/src/RemixDevTools/context/rdtReducer.ts b/src/RemixDevTools/context/rdtReducer.ts
index 2d145fe..e6258e1 100644
--- a/src/RemixDevTools/context/rdtReducer.ts
+++ b/src/RemixDevTools/context/rdtReducer.ts
@@ -6,7 +6,6 @@ export type RouteWildcards = Record | undefined>;
export type RemixDevToolsState = {
timeline: TimelineEvent[];
- showRouteBoundaries?: boolean;
terminals: Terminal[];
settings: {
routeWildcards: RouteWildcards;
@@ -20,7 +19,6 @@ export type RemixDevToolsState = {
export const initialState: RemixDevToolsState = {
timeline: [],
- showRouteBoundaries: false,
terminals: [{ id: 0, locked: false, output: [], history: [] }],
settings: {
routeWildcards: {},
diff --git a/src/RemixDevTools/context/useRDTContext.ts b/src/RemixDevTools/context/useRDTContext.ts
index c7255aa..0fa63aa 100644
--- a/src/RemixDevTools/context/useRDTContext.ts
+++ b/src/RemixDevTools/context/useRDTContext.ts
@@ -11,8 +11,7 @@ const useRDTContext = () => {
throw new Error("useRDTContext must be used within a RDTContextProvider");
}
const { state, dispatch } = context;
- const { timeline, settings, showRouteBoundaries, terminals, persistOpen } =
- state;
+ const { timeline, settings, terminals, persistOpen } = state;
const { activeTab, shouldConnectWithForge, routeWildcards, port, height } =
settings;
@@ -140,7 +139,6 @@ const useRDTContext = () => {
routeWildcards,
port,
height,
- showRouteBoundaries,
setHeight,
setRouteWildcards,
terminals,
diff --git a/src/RemixDevTools/hooks/useOutletAugment.tsx b/src/RemixDevTools/hooks/useOutletAugment.tsx
deleted file mode 100644
index b5daaff..0000000
--- a/src/RemixDevTools/hooks/useOutletAugment.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { useSubmit, useLocation } from "@remix-run/react";
-import clsx from "clsx";
-import { useEffect, useMemo } from "react";
-
-const isHooked = Symbol("isHooked");
-export function useOutletAugment(shouldAugment: boolean) {
- const submit = useSubmit();
- const location = useLocation();
- const searchParams = useMemo(
- () => new URLSearchParams(location.search),
- [location]
- );
- useEffect(() => {
- if (!shouldAugment) return;
- if (window.__remixRouteModules[isHooked as any]) return;
-
- window.__remixRouteModules = new Proxy(window.__remixRouteModules, {
- get: function (target, property) {
- if (property === isHooked) return target[property as any];
- if (property === "root") return target[property];
- const value = target[property as any];
-
- if (value?.default && value.default.name !== "hooked") {
- return {
- ...value,
- default: function hooked() {
- return (
- <>
-
-
- >
- );
- },
- };
- }
-
- return value;
- },
- });
-
- submit(searchParams, {
- method: "get",
- action: location.pathname,
- });
- // We only want to run this once regardless of the dependencies and we want it to run after the first render
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-}
diff --git a/src/RemixDevTools/index.ts b/src/RemixDevTools/index.ts
index 2b8f456..7733d13 100644
--- a/src/RemixDevTools/index.ts
+++ b/src/RemixDevTools/index.ts
@@ -1,2 +1,6 @@
export { RemixDevTools } from "./RemixDevTools";
export { useRemixForgeSocketExternal as useRemixForgeSocket } from "./hooks/useRemixForgeSocket";
+export {
+ initRouteBoundariesClient,
+ initRouteBoundariesServer,
+} from "./methods/boundaries";
diff --git a/src/RemixDevTools/methods/boundaries.tsx b/src/RemixDevTools/methods/boundaries.tsx
new file mode 100644
index 0000000..620871b
--- /dev/null
+++ b/src/RemixDevTools/methods/boundaries.tsx
@@ -0,0 +1,64 @@
+import { EntryContext } from "@remix-run/server-runtime";
+import clsx from "clsx";
+
+export const initRouteBoundariesServer = (context: EntryContext) => {
+ return {
+ ...context,
+ routeModules: Object.entries(context.routeModules).reduce(
+ (acc, [key, value]) => {
+ if (key === "root") {
+ return { ...acc, [key]: value };
+ }
+ return {
+ ...acc,
+ [key]: {
+ ...value,
+ default: () => {
+ return (
+ <>
+
+
+ >
+ );
+ },
+ },
+ };
+ },
+ {}
+ ),
+ };
+};
+
+export const initRouteBoundariesClient = () => {
+ window.__remixRouteModules = new Proxy(window.__remixRouteModules, {
+ get: function (target, property) {
+ if (property === "root") return target[property];
+ const value = target[property as any];
+ if (value?.default && value.default.name !== "hooked") {
+ return {
+ ...value,
+ default: function hooked() {
+ return (
+ <>
+
+
+ >
+ );
+ },
+ };
+ }
+
+ return value;
+ },
+ });
+};
diff --git a/src/RemixDevTools/tabs/PageTab.tsx b/src/RemixDevTools/tabs/PageTab.tsx
index c81214b..cd77644 100644
--- a/src/RemixDevTools/tabs/PageTab.tsx
+++ b/src/RemixDevTools/tabs/PageTab.tsx
@@ -7,7 +7,6 @@ import { Tag } from "../components/Tag";
import { VsCodeButton } from "../components/VScodeButton";
import { useMemo } from "react";
import { isLayoutRoute } from "../utils/routing";
-import { useRDTContext } from "../context/useRDTContext";
export const ROUTE_COLORS: Record = {
ROUTE: "rdt-bg-green-500 rdt-text-white",
@@ -46,9 +45,7 @@ const PageTab = () => {
const reversed = useMemo(() => routes.reverse(), [routes]);
const { revalidate, state } = useRevalidator();
const { isConnected, sendJsonMessage } = useRemixForgeSocket();
- const { showRouteBoundaries } = useRDTContext();
const onHover = (path: string, type: "enter" | "leave") => {
- if (!showRouteBoundaries) return;
const classes =
"rdt-bg-green-100 rdt-transition-all rdt-rounded rdt-apply-tw rdt-bg-gradient-to-r rdt-from-cyan-500/50 rdt-to-blue-500/50";
const isRoot = path === "root";
diff --git a/src/index.ts b/src/index.ts
index a05a38f..c067354 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,15 @@
import "./input.css";
-import { RemixDevTools, useRemixForgeSocket } from "./RemixDevTools";
-export { RemixDevTools, useRemixForgeSocket };
+import {
+ RemixDevTools,
+ useRemixForgeSocket,
+ initRouteBoundariesClient,
+ initRouteBoundariesServer,
+} from "./RemixDevTools";
+export {
+ RemixDevTools,
+ useRemixForgeSocket,
+ initRouteBoundariesClient,
+ initRouteBoundariesServer,
+};
//export * from "./monitor";
export default RemixDevTools;
diff --git a/src/remix-app-for-testing/app/entry.client.tsx b/src/remix-app-for-testing/app/entry.client.tsx
index 94d5dc0..3e6c738 100644
--- a/src/remix-app-for-testing/app/entry.client.tsx
+++ b/src/remix-app-for-testing/app/entry.client.tsx
@@ -4,15 +4,31 @@
* For more information, see https://remix.run/file-conventions/entry.client
*/
-import { RemixBrowser } from "@remix-run/react";
+import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
-import { hydrateRoot } from "react-dom/client";
+import { hydrateRoot } from "react-dom/client";
-startTransition(() => {
- hydrateRoot(
- document,
-
-
-
- );
-});
+if(process.env.NODE_ENV === "development") {
+ import("remix-development-tools").then(({ initRouteBoundariesClient }) => {
+ initRouteBoundariesClient();
+ startTransition(() => {
+ hydrateRoot(
+ document,
+
+
+
+ );
+ });
+
+ });
+} else {
+ startTransition(() => {
+ hydrateRoot(
+ document,
+
+
+
+ );
+ });
+
+}
\ No newline at end of file
diff --git a/src/remix-app-for-testing/app/entry.server.tsx b/src/remix-app-for-testing/app/entry.server.tsx
index b4363f1..6be8f9a 100644
--- a/src/remix-app-for-testing/app/entry.server.tsx
+++ b/src/remix-app-for-testing/app/entry.server.tsx
@@ -83,19 +83,21 @@ function handleBotRequest(
setTimeout(abort, ABORT_DELAY);
});
-}
-
+}
+
function handleBrowserRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
- return new Promise((resolve, reject) => {
+ return new Promise(async (resolve, reject) => {
let shellRendered = false;
+ const devTools = await import("remix-development-tools");
+ const context = process.env.NODE_ENV === "development" ? devTools.initRouteBoundariesServer(remixContext) : remixContext;
const { pipe, abort } = renderToPipeableStream(
,
diff --git a/src/remix-app-for-testing/app/root.tsx b/src/remix-app-for-testing/app/root.tsx
index ac124e6..35417d1 100644
--- a/src/remix-app-for-testing/app/root.tsx
+++ b/src/remix-app-for-testing/app/root.tsx
@@ -86,7 +86,6 @@ export default function App() {
)}