fix: React Refresh has trouble inferring router exports

This commit is contained in:
cha0s 2024-02-18 23:10:28 -06:00
parent b9dfea12d5
commit 7113d4db53
2 changed files with 34 additions and 7 deletions

View File

@ -4,6 +4,8 @@ import {
RouterProvider,
} from 'react-router-dom';
import {performReactRefresh} from 'react-refresh/runtime';
export const hooks = {
'@flecks/core.hmr.hook': (hook, fleck, flecks) => {
if ('@flecks/react/router.routes' === hook) {
@ -48,17 +50,23 @@ export const mixin = (Flecks) => class FlecksWithReactRouterClient extends Fleck
constructor(runtime) {
super(runtime);
const flecks = this;
let debounceRefresh;
this.reactRouter = {
invalidate() {
const {root} = flecks.get('@flecks/react/router');
if (debounceRefresh) {
return;
}
// Sorry.
setTimeout(() => {
debounceRefresh = setTimeout(() => {
const {root} = flecks.get('@flecks/react/router');
Promise.resolve(flecks.invokeFleck('@flecks/react/router.routes', root))
.then((routes) => {
// eslint-disable-next-line no-underscore-dangle
this.router._internalSetRoutes(routes);
performReactRefresh();
debounceRefresh = undefined;
});
}, 20);
}, 10);
},
router: undefined,
};

View File

@ -1,5 +1,7 @@
import {resolve} from 'path';
import {register} from 'react-refresh/runtime';
function filePathToSegmentPath(path) {
let localPath = path;
let suffix = '';
@ -34,16 +36,24 @@ export async function createRoutesFromFiletree({importer, paths, resolver}) {
// Build initial router object.
Object.entries(moduleMap)
.map(([, paths]) => (
// Second-longest: path without extension.
paths
.sort((l, r) => (l.length < r.length ? -1 : 1))
.slice(0, -1)
.pop()
))
.sort((l, r) => (l < r ? -1 : 1))
.forEach((path) => {
.map((path) => {
let trimmed = path;
// @todo dynamic
const extensions = ['.jsx', '.js', '.tsx', '.ts', '.mdx'];
const extension = extensions.find((ext) => path.endsWith(ext));
if (extension) {
trimmed = path.slice(0, -extension.length);
}
return [path, trimmed];
})
.forEach(([path, trimmed]) => {
let walk = children;
const parts = ['/', ...resolve('/', path).split('/').slice(1)];
const parts = ['/', ...resolve('/', trimmed).split('/').slice(1)];
const segments = parts.map(filePathToSegmentPath);
for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
@ -90,6 +100,10 @@ export async function createRoutesFromFiletree({importer, paths, resolver}) {
await Promise.all(
elements.map(async ([children, node, path]) => {
const route = await importer(path);
// React Refresh has trouble inferring component types when modules are structured as React
// Router prefers (e.g. `const export index = true` not being a component export). We'll give
// it a helping hand.
register(route, `${path} %exports%`);
let {hoist} = route;
if (hoist > 0) {
const parts = node.path.split('/');
@ -108,6 +122,11 @@ export async function createRoutesFromFiletree({importer, paths, resolver}) {
case 'children':
// ???
return;
case 'default':
if (!route.Component) {
node.Component = value;
}
return;
case 'hoist':
return;
case 'index':