Search code examples
javascriptreactjstypescriptbun

Check if default export of a file is a React function component or a regular function


I'm trying to determine which modules export React function components vs. regular functions. Bun uses file-based routing, so I can match the request to determine which module file to dynamically import based on the request pathname.

Let's say I have 2 files:

react-route.tsx:

export default () => {
    return <h1>hello, world</h1>
}

api-route.ts:

export default () => {
    return Response.json({ hello: 'world' })
}

I import the module like so in my Bun server:

const router = new Bun.FileSystemRouter({
    style: 'nextjs',
    dir: './app/routes',
})

const match = router.match(req)

if (!match) {
    return new Response('404')
}

const route = await import(match.filePath)

// is route.default a react function component or a regular function?

If route.default in the example above is a React function, then I want to call renderToString(<route.default />) and use that for the Bun response. Otherwise, I want to just call route.default() since it's a normal function.

I've tried using React's isValidElement but it always returns false. I'm guessing because I'd have to wrap it in a tag first, but if it isn't an actual React function, that will cause problems.

What is the best way to determine if the module's default export function is a React function or just a regular function?


Solution

  • You would not be able to determine the return type of the function at runtime without calling it.

    You could attach Symbols to the routes to be able to determine the type?

    const PAGE_ROUTE = Symbol('PAGE_ROUTE');
    
    type PageFn<T extends unknown> = T & { __routeType: typeof PAGE_ROUTE };
    
    function pageRoute<T extends unknown>(fn: T): PageFn<T> {
        const pageFn = fn as PageFn<T>;
    
        pageFn.__routeType = PAGE_ROUTE;
    
        return pageFn;
    }
    
    const API_ROUTE = Symbol('API_ROUTE');
    
    type ApiFn<T extends unknown> = T & { __routeType: typeof API_ROUTE };
    
    function apiRoute<T extends unknown>(fn: T): ApiFn<T> {
        const apiFn = fn as ApiFn<T>;
    
        apiFn.__routeType = API_ROUTE;
    
        return apiFn;
    }
    
    /* ---------------------------------------------------------------------------------------------- */
    
    const page = pageRoute(() => null);
    
    const api = apiRoute(() => null);
    
    [page, api].forEach((fn) => {
        if (fn.__routeType === PAGE_ROUTE) {
            console.log('Page route');
        } else if (fn.__routeType === API_ROUTE) {
            console.log('API route');
        }
    });
    

    TS Playground.