one
Version:
One is a new React Framework that makes Vite serve both native and web.
448 lines (444 loc) • 15.8 kB
JavaScript
import { LOADER_JS_POSTFIX_UNCACHED } from "./constants.native.js";
import { getPathFromLoaderPath } from "./utils/cleanUrl.native.js";
import { isResponse } from "./utils/isResponse.native.js";
import { getManifest } from "./vite/getManifest.native.js";
import { resolveAPIEndpoint, resolveResponse } from "./vite/resolveResponse.native.js";
function _type_of(obj) {
"@swc/helpers - typeof";
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
}
var debugRouter = process.env.ONE_DEBUG_ROUTER;
function ensureResponse(value) {
if (isResponse(value)) return value;
if (typeof value === "string") {
return new Response(value, {
headers: {
"Content-Type": "text/html"
}
});
}
if (value && (typeof value === "undefined" ? "undefined" : _type_of(value)) === "object") {
return Response.json(value);
}
return new Response(value);
}
async function runMiddlewares(handlers, request, route, getResponse) {
var middlewares = route.middlewares;
if (!(middlewares === null || middlewares === void 0 ? void 0 : middlewares.length)) {
return await getResponse();
}
if (!handlers.loadMiddleware) {
throw new Error(`No middleware handler configured`);
}
if (debugRouter) {
console.info(`[one] \u{1F517} middleware chain (${middlewares.length}) for ${route.page}`);
}
var context = {};
async function dispatch(index) {
var _this;
var middlewareModule = middlewares[index];
if (!middlewareModule) {
if (debugRouter) {
console.info(`[one] \u2713 middleware chain complete`);
}
return ensureResponse(await getResponse());
}
if (debugRouter) {
console.info(`[one] \u2192 middleware[${index}]: ${middlewareModule.contextKey}`);
}
var exported = (_this = await handlers.loadMiddleware(middlewareModule)) === null || _this === void 0 ? void 0 : _this.default;
if (!exported) {
throw new Error(`No valid export found in middleware: ${middlewareModule.contextKey}`);
}
var next = async function () {
return dispatch(index + 1);
};
var response = await exported({
request,
next,
context
});
if (response) {
if (debugRouter) {
console.info(`[one] \u2190 middleware[${index}] returned early (status: ${response.status})`);
}
return response;
}
return dispatch(index + 1);
}
return dispatch(0);
}
async function resolveAPIRoute(handlers, request, url, route) {
var {
pathname
} = url;
var params = getRouteParams(pathname, route);
if (debugRouter) {
console.info(`[one] \u{1F4E1} API ${request.method} ${pathname} \u2192 ${route.file}`, params);
}
return await runMiddlewares(handlers, request, route, async function () {
try {
return resolveAPIEndpoint(function () {
return handlers.handleAPI({
request,
route,
url,
loaderProps: {
path: pathname,
search: url.search,
subdomain: getSubdomain(url),
params
}
});
}, request, params || {});
} catch (err) {
if (isResponse(err)) {
return err;
}
if (process.env.NODE_ENV === "development") {
console.error(`
[one] Error importing API route at ${pathname}:
${err}
If this is an import error, you can likely fix this by adding this dependency to
the "optimizeDeps.include" array in your vite.config.ts.
`);
}
throw err;
}
});
}
async function resolveLoaderRoute(handlers, request, url, route) {
if (debugRouter) {
console.info(`[one] \u{1F4E6} loader ${url.pathname} \u2192 ${route.file}`);
}
var isNativeRequest = url.searchParams.get("platform") === "ios" || url.searchParams.get("platform") === "android";
var response = await runMiddlewares(handlers, request, route, async function () {
return await resolveResponse(async function () {
var headers = new Headers();
headers.set("Content-Type", "text/javascript");
try {
var loaderResponse = await handlers.handleLoader({
request,
route,
url,
loaderProps: {
path: url.pathname,
search: url.search,
subdomain: getSubdomain(url),
request: route.type === "ssr" ? request : void 0,
params: getLoaderParams(url, route)
}
});
var body2 = isNativeRequest && loaderResponse ? toCjsLoader(loaderResponse) : loaderResponse;
return new Response(body2, {
headers
});
} catch (err) {
if (isResponse(err)) {
return err;
}
if ((err === null || err === void 0 ? void 0 : err.code) !== "ERR_MODULE_NOT_FOUND") {
console.error(`Error running loader: ${err}`);
}
throw err;
}
});
});
if (response.status >= 300 && response.status < 400) {
var location = response.headers.get("location");
if (location) {
var redirectUrl = new URL(location, url.origin);
var redirectPath = redirectUrl.pathname + redirectUrl.search + redirectUrl.hash;
var data = `{__oneRedirect:${JSON.stringify(redirectPath)},__oneRedirectStatus:${response.status}}`;
var body = isNativeRequest ? `exports.loader=function(){return ${data}}` : `export function loader(){return${data}}`;
return new Response(body, {
headers: {
"Content-Type": "text/javascript"
}
});
}
}
if (response.status === 401 || response.status === 403) {
var data1 = `{__oneError:${response.status},__oneErrorMessage:${JSON.stringify(response.statusText || "Unauthorized")}}`;
var body1 = isNativeRequest ? `exports.loader=function(){return ${data1}}` : `export function loader(){return${data1}}`;
return new Response(body1, {
headers: {
"Content-Type": "text/javascript"
}
});
}
return response;
}
function toCjsLoader(esmCode) {
if (esmCode.startsWith("exports.")) {
return esmCode;
}
var match = esmCode.match(/export\s+function\s+loader\s*\(\)\s*\{\s*return\s+([\s\S]+)\s*\}/);
if (match) {
return `exports.loader=function(){return ${match[1]}}`;
}
return `exports.loader=function(){return {}}`;
}
async function resolvePageRoute(handlers, request, url, route) {
var _route_middlewares;
var {
pathname,
search
} = url;
if (debugRouter) {
console.info(`[one] \u{1F4C4} page ${pathname} \u2192 ${route.file} (${route.type})`);
}
var loaderProps = {
path: pathname,
search,
subdomain: getSubdomain(url),
request: route.type === "ssr" ? request : void 0,
params: getLoaderParams(url, route)
};
if (!((_route_middlewares = route.middlewares) === null || _route_middlewares === void 0 ? void 0 : _route_middlewares.length)) {
return resolveResponse(function () {
return handlers.handlePage({
request,
route,
url,
loaderProps
});
});
}
return resolveResponse(async function () {
return await runMiddlewares(handlers, request, route, async function () {
return await handlers.handlePage({
request,
route,
url,
loaderProps
});
});
});
}
var _urlCache = /* @__PURE__ */new WeakMap();
function getURLfromRequestURL(request) {
var url = _urlCache.get(request);
if (url) return url;
var urlString = request.url || "";
url = new URL(urlString || "", request.headers.get("host") ? `http://${request.headers.get("host")}` : "");
_urlCache.set(request, url);
return url;
}
function getSubdomain(url) {
var host = url.hostname;
if (!host || host === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(host)) {
return void 0;
}
var parts = host.split(".");
if (parts.length < 3) {
return void 0;
}
return parts.slice(0, -2).join(".");
}
function compileRouteRegex(route) {
return {
...route,
compiledRegex: new RegExp(route.namedRegex)
};
}
function compileManifest(manifest) {
return {
pageRoutes: manifest.pageRoutes.map(compileRouteRegex),
apiRoutes: manifest.apiRoutes.map(compileRouteRegex)
};
}
function createHandleRequest(handlers, param) {
var {
routerRoot,
ignoredRouteFiles
} = param;
var manifest = getManifest({
routerRoot,
ignoredRouteFiles
});
if (!manifest) {
throw new Error(`No routes manifest`);
}
var compiledManifest = compileManifest(manifest);
return {
manifest,
handler: async function handleRequest(request) {
var url = getURLfromRequestURL(request);
var {
pathname,
search
} = url;
if (pathname === "/__vxrnhmr" || pathname.startsWith("/@vite/") || pathname.startsWith("/@fs/") || pathname.startsWith("/@id/") || pathname.startsWith("/node_modules/") || pathname.startsWith("/debugger-frontend") || pathname.startsWith("/inspector")) {
return null;
}
var looksLikeStaticFile = !pathname.endsWith(LOADER_JS_POSTFIX_UNCACHED) && /\.[a-zA-Z0-9]{2,4}$/.test(pathname);
if (handlers.handleAPI) {
var apiRoute = compiledManifest.apiRoutes.find(function (route2) {
return route2.compiledRegex.test(pathname);
});
if (apiRoute) {
if (debugRouter) {
console.info(`[one] \u26A1 ${pathname} \u2192 matched API route: ${apiRoute.page}`);
}
return await resolveAPIRoute(handlers, request, url, apiRoute);
}
}
if (request.method !== "GET") {
return null;
}
if (handlers.handleLoader) {
var isClientRequestingNewRoute = pathname.endsWith(LOADER_JS_POSTFIX_UNCACHED);
if (isClientRequestingNewRoute) {
var platformParam = url.searchParams.get("platform");
var isNativePlatform = platformParam === "ios" || platformParam === "android" || platformParam === "native";
if (isNativePlatform && handlers.handleStaticFile) {
var nativeLoaderPath = pathname.replace(/\.js$/, ".native.js");
var staticResponse = await handlers.handleStaticFile(nativeLoaderPath);
if (staticResponse) {
return staticResponse;
}
}
var originalUrl = getPathFromLoaderPath(pathname);
var _iteratorNormalCompletion = true,
_didIteratorError = false,
_iteratorError = void 0;
try {
for (var _iterator = compiledManifest.pageRoutes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var route = _step.value;
if (route.file === "") {
continue;
}
var finalUrl = new URL(originalUrl, url.origin);
finalUrl.search = url.search;
if (!route.compiledRegex.test(finalUrl.pathname)) {
continue;
}
if (route.hasLoader === false) {
var emptyBody = isNativePlatform ? "exports.loader=function(){return undefined}" : "export function loader() { return undefined }";
return new Response(emptyBody, {
headers: {
"Content-Type": "text/javascript"
}
});
}
var cleanedRequest = new Request(finalUrl, request);
return resolveLoaderRoute(handlers, cleanedRequest, finalUrl, route);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var emptyBody1 = isNativePlatform ? "exports.loader=function(){return{}}" : "export {}";
return new Response(emptyBody1, {
headers: {
"Content-Type": "text/javascript"
}
});
}
}
if (handlers.handlePage) {
var _iteratorNormalCompletion1 = true,
_didIteratorError1 = false,
_iteratorError1 = void 0;
try {
for (var _iterator1 = compiledManifest.pageRoutes[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true) {
var route1 = _step1.value;
if (!route1.compiledRegex.test(pathname)) {
continue;
}
var isDynamicRoute = Object.keys(route1.routeKeys).length > 0;
var isNotFoundRoute = route1.page.endsWith("/+not-found");
if (looksLikeStaticFile && isDynamicRoute && !isNotFoundRoute) {
if (debugRouter) {
console.info(`[one] \u26A1 ${pathname} \u2192 skipping dynamic route ${route1.page} for static-looking path`);
}
continue;
}
if (looksLikeStaticFile && route1.file === "") {
if (debugRouter) {
console.info(`[one] \u26A1 ${pathname} \u2192 404 for probe path (no +not-found defined)`);
}
return new Response(null, {
status: 404,
headers: {
"Content-Type": "text/plain"
}
});
}
if (debugRouter) {
console.info(`[one] \u26A1 ${pathname} \u2192 matched page route: ${route1.page} (${route1.type})`);
}
return resolvePageRoute(handlers, request, url, route1);
}
} catch (err) {
_didIteratorError1 = true;
_iteratorError1 = err;
} finally {
try {
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
_iterator1.return();
}
} finally {
if (_didIteratorError1) {
throw _iteratorError1;
}
}
}
}
return null;
}
};
}
function getLoaderParams(url, config) {
var params = {};
var match = config.compiledRegex.exec(url.pathname);
if (match === null || match === void 0 ? void 0 : match.groups) {
var _iteratorNormalCompletion = true,
_didIteratorError = false,
_iteratorError = void 0;
try {
for (var _iterator = Object.entries(match.groups)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var [key, value] = _step.value;
var namedKey = config.routeKeys[key];
params[namedKey] = value;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
return params;
}
function getRouteParams(pathname, route) {
var regex = new RegExp(route.namedRegex);
var match = regex.exec(pathname);
if (!match) return {};
return Object.fromEntries(Object.entries(route.routeKeys).map(function (param) {
var [key, value] = param;
var _match_groups;
return [value, ((_match_groups = match.groups) === null || _match_groups === void 0 ? void 0 : _match_groups[key]) || ""];
}));
}
export { compileManifest, createHandleRequest, getLoaderParams, getSubdomain, getURLfromRequestURL, resolveAPIRoute, resolveLoaderRoute, resolvePageRoute, runMiddlewares };
//# sourceMappingURL=createHandleRequest.native.js.map