one
Version:
One is a new React Framework that makes Vite serve both native and web.
198 lines (197 loc) • 6.73 kB
JavaScript
import { hmrImport } from "./hmrImport.mjs";
let lastVersion = 0;
let context;
function useViteRoutes(routes, routerRoot, options, version) {
if (version && version > lastVersion) {
context = null;
lastVersion = version;
for (const key of Object.keys(preloadedModules)) {
delete preloadedModules[key];
}
}
if (!context) {
loadRoutes(routes, routerRoot, options);
}
return context;
}
const preloadedModules = {};
function registerPreloadedRoute(key, module) {
preloadedModules[key] = module;
}
function getPreloadedModule(key) {
return preloadedModules[key];
}
function getPreloadedModuleKeys() {
return Object.keys(preloadedModules);
}
function matchDynamicRoute(routePattern, actualPath) {
const routeSegments = routePattern.split("/");
const pathSegments = actualPath.split("/");
const hasCatchAll = routeSegments.some(s => s.startsWith("[..."));
if (hasCatchAll) {
const catchAllIdx = routeSegments.findIndex(s => s.startsWith("[..."));
for (let i = 0; i < catchAllIdx; i++) {
if (!routeSegments[i]) continue;
if (routeSegments[i].startsWith("[")) continue;
if (routeSegments[i] !== pathSegments[i]) return false;
}
return pathSegments.length >= catchAllIdx;
}
if (routeSegments.length !== pathSegments.length) return false;
for (let i = 0; i < routeSegments.length; i++) {
const routeSeg = routeSegments[i];
const pathSeg = pathSegments[i];
if (routeSeg.startsWith("[") && routeSeg.endsWith("]")) {
continue;
}
if (routeSeg !== pathSeg) return false;
}
return true;
}
async function preloadRouteModules(href) {
const globbed = globalThis["__importMetaGlobbed"];
if (!globbed) return;
const normalizedHref = href === "/" ? "" : href.replace(/^\//, "").replace(/\/$/, "");
const promises = [];
for (const key of Object.keys(globbed)) {
let routePath = key.replace(/^\/[^/]+\//, "");
routePath = routePath.replace(/\([^)]+\)\//g, "");
routePath = routePath.replace(/\/_layout\.tsx$/, "").replace(/\/index(\+[a-z]+)?\.tsx$/, "").replace(/(\+[a-z]+)?\.tsx$/, "");
routePath = routePath.replace(/^\//, "");
const isStaticMatch = routePath === normalizedHref ||
// exact match
routePath.startsWith(normalizedHref + "/") ||
// child route
normalizedHref.startsWith(routePath + "/") ||
// parent layout
routePath === "" ||
// root layout
normalizedHref !== "" && routePath === normalizedHref.split("/")[0];
const isDynamicMatch = routePath.includes("[") && matchDynamicRoute(routePath, normalizedHref);
if ((isStaticMatch || isDynamicMatch) && typeof globbed[key] === "function") {
promises.push(globbed[key]().then(mod => {
preloadedModules[key] = mod;
}).catch(() => {}));
}
}
await Promise.all(promises);
}
function loadRoutes(paths, routerRoot, options) {
if (context) return context;
globalThis["__importMetaGlobbed"] = paths;
context = globbedRoutesToRouteContext(paths, routerRoot, options);
return context;
}
function globbedRoutesToRouteContext(paths, routerRoot, options) {
const routesSync = {};
const routePaths = {};
const promises = {};
const loadedRoutes = {};
const clears = {};
Object.keys(paths).forEach(path => {
if (!paths[path]) {
console.error(`Error: Missing route at path ${path}`);
return;
}
const loadRouteFunction = paths[path];
const pathWithoutRelative = path.replace(`/${routerRoot}/`, "./");
const originalPath = pathWithoutRelative.slice(1).replace(/\.[jt]sx?$/, "");
if (options?.routeModes?.[originalPath] === "spa") {
console.info(`Spa mode: ${originalPath}`);
loadedRoutes[pathWithoutRelative] = () => {
return null;
};
} else {
routesSync[pathWithoutRelative] = loadRouteFunction;
routePaths[pathWithoutRelative] = path;
}
});
const moduleKeys = Object.keys(routesSync);
let hmrVersion = 0;
if (typeof window !== "undefined") {
;
window.__oneRouteCache = {
clear: () => {
hmrVersion++;
Object.keys(loadedRoutes).forEach(key => {
delete loadedRoutes[key];
});
Object.keys(promises).forEach(key => {
delete promises[key];
});
Object.keys(preloadedModules).forEach(key => {
delete preloadedModules[key];
});
},
clearFile: file => {
hmrVersion++;
const normalizedFile = file.replace(/^app\//, "./");
Object.keys(loadedRoutes).forEach(key => {
if (key === normalizedFile || key.endsWith(normalizedFile.replace("./", "/"))) {
delete loadedRoutes[key];
}
});
Object.keys(promises).forEach(key => {
if (key === normalizedFile || key.endsWith(normalizedFile.replace("./", "/"))) {
delete promises[key];
}
});
Object.keys(preloadedModules).forEach(key => {
if (key.includes(file)) {
delete preloadedModules[key];
}
});
},
getVersion: () => hmrVersion
};
}
function resolve(id) {
clearTimeout(clears[id]);
if (loadedRoutes[id]) {
return loadedRoutes[id];
}
const preloadKey = id.replace("./", `/${routerRoot}/`);
const preloaded = getPreloadedModule(preloadKey);
if (preloaded) {
loadedRoutes[id] = preloaded;
return preloaded;
}
if (typeof routesSync[id] !== "function") {
return routesSync[id];
}
if (!promises[id]) {
let importPromise;
if (process.env.NODE_ENV === "development" && hmrVersion > 0 && routePaths[id]) {
importPromise = hmrImport(routePaths[id]).catch(() => routesSync[id]());
} else {
importPromise = routesSync[id]();
}
promises[id] = importPromise.then(val => {
loadedRoutes[id] = val;
delete promises[id];
if (process.env.NODE_ENV === "development") {
clears[id] = setTimeout(() => {
delete loadedRoutes[id];
}, 500);
}
return val;
}).catch(err => {
console.error(`Error loading route`, id, err, new Error().stack);
loadedRoutes[id] = {
default: () => null
};
delete promises[id];
});
if (process.env.NODE_ENV === "development") {
promises[id].stack = new Error().stack;
}
}
throw promises[id];
}
resolve.keys = () => moduleKeys;
resolve.id = "";
resolve.resolve = id => id;
return resolve;
}
export { getPreloadedModule, getPreloadedModuleKeys, globbedRoutesToRouteContext, loadRoutes, preloadRouteModules, registerPreloadedRoute, useViteRoutes };
//# sourceMappingURL=useViteRoutes.mjs.map