UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

282 lines (273 loc) 10.3 kB
import { join } from "path"; import FSExtra from "fs-extra"; import * as constants from "../constants.native.js"; import { getLoaderPath, getPreloadCSSPath, getPreloadPath } from "../utils/cleanUrl.native.js"; import { isResponse } from "../utils/isResponse.native.js"; import { toAbsolute } from "../utils/toAbsolute.native.js"; import { replaceLoader } from "../vite/replaceLoader.native.js"; function _instanceof(left, right) { return right != null && typeof Symbol < "u" && right[Symbol.hasInstance] ? !!right[Symbol.hasInstance](left) : left instanceof right; } var { readFile, outputFile } = FSExtra; function urlPathToFilePath(urlPath) { var parts = urlPath.replace(/^\//, "").split("/"); return join(...parts); } async function buildPage(serverEntry, path, relativeId, params, foundRoute, clientManifestEntry, staticDir, clientDir, builtMiddlewares, serverJsPath, preloads, allCSS, routePreloads, allCSSContents, criticalPreloads, deferredPreloads, useAfterLCP, useAfterLCPAggressive) { var render = await getRender(serverEntry), htmlPath = `${path.endsWith("/") ? `${removeTrailingSlash(path)}/index` : path}.html`, clientJsPath = join("dist/client", clientManifestEntry.file), htmlOutPath = toAbsolute(join(staticDir, htmlPath)), preloadPath = getPreloadPath(path), cssPreloadPath = getPreloadCSSPath(path), loaderPath = "", loaderData = {}; try { var routeImports = [], routeRegistrations = [], routeIndex = 0, _iteratorNormalCompletion = !0, _didIteratorError = !1, _iteratorError = void 0; try { for (var _iterator = Object.entries(routePreloads)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = !0) { var [routeKey, bundlePath] = _step.value, varName = `_r${routeIndex++}`; routeImports.push(`import * as ${varName} from "${bundlePath}"`), routeRegistrations.push(`registerPreloadedRoute("${routeKey}", ${varName})`); } } catch (err) { _didIteratorError = !0, _iteratorError = err; } finally { try { !_iteratorNormalCompletion && _iterator.return != null && _iterator.return(); } finally { if (_didIteratorError) throw _iteratorError; } } var registrationCalls = routeRegistrations.map(function (call) { return call.replace("registerPreloadedRoute(", "window.__oneRegisterPreloadedRoute("); }), preloadContent = [ // import all route modules ...routeImports, // static imports for cache warming (original behavior) ...preloads.map(function (preload) { return `import "${preload}"`; }), // register all route modules using window global ...registrationCalls].join(` `); await FSExtra.writeFile(join(clientDir, urlPathToFilePath(preloadPath)), preloadContent); var uniqueCSS = [...new Set(allCSS)], cssPreloadContent = ` const CSS_TIMEOUT = 1000 const cssUrls = ${JSON.stringify(uniqueCSS)} // Global cache for loaded CSS - avoids DOM queries and tracks across navigations const loaded = (window.__oneLoadedCSS ||= new Set()) // Prefetch CSS without applying - called on link hover export function prefetchCSS() { cssUrls.forEach(href => { if (loaded.has(href)) return if (document.querySelector(\`link[href="\${href}"]\`)) return const link = document.createElement('link') link.rel = 'prefetch' link.as = 'style' link.href = href document.head.appendChild(link) }) } // Inject CSS to apply styles - called on actual navigation export function injectCSS() { return Promise.all(cssUrls.map(href => { // Skip if already loaded if (loaded.has(href)) return Promise.resolve() // Remove any prefetch link for this href const prefetchLink = document.querySelector(\`link[rel="prefetch"][href="\${href}"]\`) if (prefetchLink) prefetchLink.remove() // Skip if stylesheet already exists in DOM if (document.querySelector(\`link[rel="stylesheet"][href="\${href}"]\`)) { loaded.add(href) return Promise.resolve() } return new Promise(resolve => { const link = document.createElement('link') link.rel = 'stylesheet' link.href = href const timeoutId = setTimeout(() => { console.warn('[one] CSS load timeout:', href) loaded.add(href) resolve() }, CSS_TIMEOUT) link.onload = link.onerror = () => { clearTimeout(timeoutId) loaded.add(href) resolve() } document.head.appendChild(link) }) })) } // For backwards compatibility, also prefetch on import prefetchCSS() `; await FSExtra.writeFile(join(clientDir, urlPathToFilePath(cssPreloadPath)), cssPreloadContent); var exported = await import(toAbsolute(serverJsPath)); if (exported.loader) { try { var _ref, _exported_loader; loaderData = (_ref = await ((_exported_loader = exported.loader) === null || _exported_loader === void 0 ? void 0 : _exported_loader.call(exported, { path, params }))) !== null && _ref !== void 0 ? _ref : null; } catch (err) { if (!isResponse(err)) throw err; } var code = await readFile(clientJsPath, "utf-8"), withLoader = // super dirty to quickly make ssr loaders work until we have better ` if (typeof document === 'undefined') globalThis.document = {} ` + replaceLoader({ code, loaderData }), loaderPartialPath = join(clientDir, urlPathToFilePath(getLoaderPath(path))); await outputFile(loaderPartialPath, withLoader), loaderPath = getLoaderPath(path); } if (foundRoute.type !== "ssr") { var _globalThis___vxrnresetState, _globalThis, loaderProps = { path, params }; if ((_globalThis___vxrnresetState = (_globalThis = globalThis).__vxrnresetState) === null || _globalThis___vxrnresetState === void 0 || _globalThis___vxrnresetState.call(_globalThis), foundRoute.type === "ssg") { var renderPreloads = criticalPreloads || preloads, renderDeferredPreloads = useAfterLCPAggressive ? [] : deferredPreloads, html = await render({ path, preloads: renderPreloads, deferredPreloads: renderDeferredPreloads, loaderProps, loaderData, css: allCSS, cssContents: allCSSContents, mode: "ssg", routePreloads }); useAfterLCP && (html = applyAfterLCPScriptLoad(html, preloads)), await outputFile(htmlOutPath, html); } else if (foundRoute.type === "spa") { var cssOutput = allCSSContents ? allCSSContents.filter(Boolean).map(function (content) { return ` <style>${content}</style>`; }).join(` `) : allCSS.map(function (file) { return ` <link rel="stylesheet" href=${file} />`; }).join(` `), criticalScripts = (criticalPreloads || preloads).map(function (preload) { return ` <script type="module" src="${preload}"></script>`; }).join(` `), deferredLinks = (deferredPreloads || []).map(function (preload) { return ` <link rel="modulepreload" fetchPriority="low" href="${preload}"/>`; }).join(` `); await outputFile(htmlOutPath, `<html><head> ${constants.getSpaHeaderElements({ serverContext: { loaderProps, loaderData } })} ${criticalScripts} ${deferredLinks} ${cssOutput} </head><body></body></html>`); } } } catch (err) { var errMsg = _instanceof(err, Error) ? `${err.message} ${err.stack}` : `${err}`; console.error(`Error building static page at ${path} with id ${relativeId}: ${errMsg} loaderData: ${JSON.stringify(loaderData || null, null, 2)} params: ${JSON.stringify(params || null, null, 2)}`), console.error(err), process.exit(1); } var middlewares = (foundRoute.middlewares || []).map(function (x) { return builtMiddlewares[x.contextKey]; }), cleanPath = path === "/" ? path : removeTrailingSlash(path); return { type: foundRoute.type, css: allCSS, cssContents: allCSSContents, routeFile: foundRoute.file, middlewares, cleanPath, preloadPath, cssPreloadPath, loaderPath, clientJsPath, serverJsPath, htmlPath, loaderData, params, path, preloads, criticalPreloads, deferredPreloads }; } async function getRender(serverEntry) { var render = null; try { var _serverImport_default_default, serverImport = await import(serverEntry); render = serverImport.default.render || ((_serverImport_default_default = serverImport.default.default) === null || _serverImport_default_default === void 0 ? void 0 : _serverImport_default_default.render), typeof render != "function" && (console.error("\u274C Error: didn't find render function in entry", serverImport), process.exit(1)); } catch (err) { console.error("\u274C Error importing the root entry:"), console.error(` This error happened in the built file: ${serverEntry}`), console.error(err.stack), process.exit(1); } return render; } function removeTrailingSlash(path) { return path.endsWith("/") ? path.slice(0, path.length - 1) : path; } function applyAfterLCPScriptLoad(html, preloads) { html = html.replace(/<script\s+type="module"[^>]*async[^>]*><\/script>/gi, ""); var loaderScript = ` <script> (function() { var scripts = ${JSON.stringify(preloads)}; function loadScripts() { scripts.forEach(function(src) { var script = document.createElement('script'); script.type = 'module'; script.src = src; document.head.appendChild(script); }); } function waitIdle(n) { if (n <= 0) { requestAnimationFrame(function() { requestAnimationFrame(loadScripts); }); return; } setTimeout(function() { setTimeout(function() { waitIdle(n - 1); }, 0); }, 0); } waitIdle(5); })(); </script>`; return html = html.replace("</head>", `${loaderScript}</head>`), html; } export { buildPage }; //# sourceMappingURL=buildPage.native.js.map