Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions packages/react-router-devtools/src/vite/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { beforeEach, describe, expect, it, vi } from "vitest"
import { reactRouterDevTools } from "./plugin"

// Mock the detect-typegen module
vi.mock("./utils/detect-typegen", () => ({
isTypegenContext: vi.fn(),
}))

describe("reactRouterDevTools", () => {
let isTypegenContext: ReturnType<typeof vi.fn>

beforeEach(async () => {
vi.clearAllMocks()
const module = await import("./utils/detect-typegen")
isTypegenContext = vi.mocked(module.isTypegenContext)
})

describe("typegen context", () => {
it("should return empty array when in typegen context", () => {
isTypegenContext.mockReturnValue(true)

const plugins = reactRouterDevTools()

expect(plugins).toEqual([])
expect(isTypegenContext).toHaveBeenCalledTimes(1)
})

it("should return empty array even with config options in typegen context", () => {
isTypegenContext.mockReturnValue(true)

const plugins = reactRouterDevTools({
client: { expansionLevel: 2 },
})

expect(plugins).toEqual([])
expect(isTypegenContext).toHaveBeenCalledTimes(1)
})
})

describe("normal context", () => {
it("should return plugin array in normal context", () => {
isTypegenContext.mockReturnValue(false)

const plugins = reactRouterDevTools()

expect(Array.isArray(plugins)).toBe(true)
expect(plugins.length).toBeGreaterThan(0)
expect(isTypegenContext).toHaveBeenCalledTimes(1)
})

it("should return multiple plugins including TanStack DevTools", () => {
isTypegenContext.mockReturnValue(false)

const plugins = reactRouterDevTools()

// TanStack DevTools returns an array that gets spread
// + 5 custom plugins = 6+ total
expect(plugins.length).toBeGreaterThan(5)

// Verify plugin names exist
const pluginNames = plugins.filter((p) => p.name).map((p) => p.name)

expect(pluginNames).toContain("react-router-devtools")
})

it("should return plugin array when config options are provided", () => {
isTypegenContext.mockReturnValue(false)

const plugins = reactRouterDevTools({
server: { silent: false },
})

expect(Array.isArray(plugins)).toBe(true)
expect(plugins.length).toBeGreaterThan(5)
})
})
})
6 changes: 6 additions & 0 deletions packages/react-router-devtools/src/vite/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { augmentDataFetchingFunctions } from "./utils/data-functions-augment.js"
import { injectRdtClient } from "./utils/inject-client.js"
import { injectContext } from "./utils/inject-context.js"
import { augmentMiddlewareFunctions } from "./utils/middleware-augment.js"
import { isTypegenContext } from "./utils/detect-typegen.js"
// this should mirror the types in server/config.ts as well as they are bundled separately.
declare global {
interface Window {
Expand Down Expand Up @@ -170,6 +171,11 @@ type Route = {
export const defineRdtConfig = (config: ReactRouterViteConfig) => config

export const reactRouterDevTools: (args?: ReactRouterViteConfig) => Plugin[] = (args) => {
// Return empty array in typegen context (disable DevTools)
if (isTypegenContext()) {
return []
}

const serverConfig = args?.server || {}
const clientConfig = {
...args?.client,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest"
import { isTypegenContext } from "./detect-typegen"

describe("isTypegenContext", () => {
const originalEnv = process.env
const originalArgv = process.argv

beforeEach(() => {
// Reset environment to clean state
process.env = { ...originalEnv }
process.argv = [...originalArgv]
})

afterEach(() => {
// Restore environment after test
process.env = originalEnv
process.argv = originalArgv
})

describe("environment variable detection", () => {
it("should return true when TYPEGEN_RUNNING=1", () => {
process.env.TYPEGEN_RUNNING = "1"
expect(isTypegenContext()).toBe(true)
})

it("should return true when SAFE_ROUTES_TYPEGEN=1", () => {
process.env.SAFE_ROUTES_TYPEGEN = "1"
expect(isTypegenContext()).toBe(true)
})

it("should return true when npm_lifecycle_event contains typegen (pre)", () => {
// cspell:disable-next-line
process.env.npm_lifecycle_event = "pretypegen"
expect(isTypegenContext()).toBe(true)
})

it("should return true when npm_lifecycle_event contains typegen (post)", () => {
// cspell:disable-next-line
process.env.npm_lifecycle_event = "posttypegen"
expect(isTypegenContext()).toBe(true)
})
})

describe("command line argument detection", () => {
it("should return true when process.argv contains typegen", () => {
process.argv = ["node", "script.js", "typegen"]
expect(isTypegenContext()).toBe(true)
})

it("should return true when process.argv contains type-gen", () => {
process.argv = ["node", "script.js", "type-gen"]
expect(isTypegenContext()).toBe(true)
})

it("should return true when process.argv contains react-router typegen", () => {
process.argv = ["node", "script.js", "react-router", "typegen"]
expect(isTypegenContext()).toBe(true)
})

it("should return true when process.argv contains safe-routes", () => {
process.argv = ["node", "script.js", "safe-routes"]
expect(isTypegenContext()).toBe(true)
})
})

describe("normal context", () => {
it("should return false when no typegen indicators are present", () => {
expect(isTypegenContext()).toBe(false)
})

it("should return false with unrelated environment variables", () => {
process.env.OTHER_VAR = "1"
expect(isTypegenContext()).toBe(false)
})

it("should return false with unrelated command line arguments", () => {
process.argv = ["node", "script.js", "dev", "build"]
expect(isTypegenContext()).toBe(false)
})

it("should return false when TYPEGEN_RUNNING=0", () => {
process.env.TYPEGEN_RUNNING = "0"
expect(isTypegenContext()).toBe(false)
})

it("should return false when TYPEGEN_RUNNING is empty string", () => {
process.env.TYPEGEN_RUNNING = ""
expect(isTypegenContext()).toBe(false)
})
})
})
37 changes: 37 additions & 0 deletions packages/react-router-devtools/src/vite/utils/detect-typegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Detects if running in a typegen context
*
* Returns true if any of the following conditions are met:
* - Environment variables: TYPEGEN_RUNNING=1, SAFE_ROUTES_TYPEGEN=1
* - npm script: npm_lifecycle_event contains "typegen"
* - Command line arguments: contains "typegen", "type-gen", "react-router typegen", or "safe-routes"
*
* @returns true if in typegen context, false otherwise
*/
export function isTypegenContext(): boolean {
// Check environment variables
if (
process.env.TYPEGEN_RUNNING === "1" ||
process.env.SAFE_ROUTES_TYPEGEN === "1"
) {
return true
}

// Check npm lifecycle event
if (process.env.npm_lifecycle_event?.includes("typegen")) {
return true
}

// Check command line arguments
const args = process.argv.join(" ")
if (
args.includes("typegen") ||
args.includes("type-gen") ||
args.includes("react-router typegen") ||
args.includes("safe-routes")
) {
return true
}

return false
}