one
Version:
One is a new React Framework that makes Vite serve both native and web.
697 lines (682 loc) • 31.2 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf,
__hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all) __defProp(target, name, {
get: all[name],
enumerable: !0
});
},
__copyProps = (to, from, except, desc) => {
if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
get: () => from[key],
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: !0
}) : target, mod)),
__toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
value: !0
}), mod);
var build_exports = {};
__export(build_exports, {
build: () => build
});
module.exports = __toCommonJS(build_exports);
var import_node_module = require("node:module"),
import_node_os = require("node:os"),
import_node_path = __toESM(require("node:path"), 1),
import_fs_extra = __toESM(require("fs-extra"), 1),
import_micromatch = __toESM(require("micromatch"), 1),
import_vite = require("vite"),
import_vxrn = require("vxrn"),
constants = __toESM(require("../constants.cjs"), 1),
import_setServerGlobals = require("../server/setServerGlobals.cjs"),
import_getPathnameFromFilePath = require("../utils/getPathnameFromFilePath.cjs"),
import_getRouterRootFromOneOptions = require("../utils/getRouterRootFromOneOptions.cjs"),
import_isRolldown = require("../utils/isRolldown.cjs"),
import_toAbsolute = require("../utils/toAbsolute.cjs"),
import_buildVercelOutputDirectory = require("../vercel/build/buildVercelOutputDirectory.cjs"),
import_getManifest = require("../vite/getManifest.cjs"),
import_loadConfig = require("../vite/loadConfig.cjs"),
import_one_server_only = require("../vite/one-server-only.cjs"),
import_buildPage = require("./buildPage.cjs"),
import_checkNodeVersion = require("./checkNodeVersion.cjs"),
import_workerPool = require("./workerPool.cjs"),
import_generateSitemap = require("./generateSitemap.cjs"),
import_label_process = require("./label-process.cjs"),
import_pLimit = require("../utils/pLimit.cjs");
const import_meta = {},
{
ensureDir,
writeJSON
} = import_fs_extra.default,
BUILD_CONCURRENCY = process.env.ONE_BUILD_CONCURRENCY ? Math.max(1, parseInt(process.env.ONE_BUILD_CONCURRENCY, 10)) : Math.max(1, Math.min((0, import_node_os.cpus)().length, 8));
function shouldUseWorkers(oneOptions) {
return process.env.ONE_BUILD_WORKERS === "0" ? !1 : process.env.ONE_BUILD_WORKERS === "1" ? !0 : oneOptions?.build?.workers !== !1;
}
process.on("uncaughtException", err => {
console.error(err?.message || err);
});
async function build(args) {
process.env.IS_VXRN_CLI = "true", process.env.NODE_ENV ? process.env.NODE_ENV !== "production" && console.warn(`
\u26A0\uFE0F Warning: NODE_ENV is set to "${process.env.NODE_ENV}" (builds default to "production")
`) : process.env.NODE_ENV = "production", (0, import_label_process.labelProcess)("build"), (0, import_checkNodeVersion.checkNodeVersion)(), (0, import_setServerGlobals.setServerGlobals)();
const {
oneOptions
} = await (0, import_loadConfig.loadUserOneOptions)("build"),
routerRoot = (0, import_getRouterRootFromOneOptions.getRouterRootFromOneOptions)(oneOptions);
oneOptions.web?.defaultRenderMode && (process.env.ONE_DEFAULT_RENDER_MODE = oneOptions.web.defaultRenderMode);
const manifest = (0, import_getManifest.getManifest)({
routerRoot
}),
serverOutputFormat = oneOptions.build?.server === !1 ? "esm" : oneOptions.build?.server?.outputFormat ?? "esm",
buildStartTime = performance.now(),
vxrnOutput = await (0, import_vxrn.build)({
skipEnv: args.skipEnv ?? oneOptions.skipEnv,
server: oneOptions.server,
build: {
analyze: !0,
server: oneOptions.build?.server === !1 ? !1 : {
outputFormat: serverOutputFormat
}
}
}, args),
bundleTime = performance.now() - buildStartTime;
if (console.info(`
\u23F1\uFE0F vite bundle: ${(bundleTime / 1e3).toFixed(2)}s
`), !vxrnOutput || args.platform !== "web") return;
const options = await (0, import_vxrn.fillOptions)(vxrnOutput.options, {
mode: "prod"
}),
{
optimizeDeps
} = (0, import_vxrn.getOptimizeDeps)("build"),
apiBuildConfig = (0, import_vite.mergeConfig)(
// feels like this should build off the *server* build config not web
vxrnOutput.webBuildConfig, {
configFile: !1,
appType: "custom",
optimizeDeps
});
async function buildCustomRoutes(subFolder, routes) {
const input = routes.reduce((entries, {
page,
file
}) => (entries[page.slice(1) + ".js"] = (0, import_node_path.join)(routerRoot, file), entries), {}),
outputFormat = oneOptions?.build?.api?.outputFormat ?? serverOutputFormat,
treeshake = oneOptions?.build?.api?.treeshake,
mergedConfig = (0, import_vite.mergeConfig)(apiBuildConfig, {
appType: "custom",
configFile: !1,
// plugins: [
// nodeExternals({
// exclude: optimizeDeps.include,
// }) as any,
// ],
define: {
...vxrnOutput.processEnvDefines
},
ssr: {
noExternal: !0,
external: ["react", "react-dom"],
optimizeDeps
},
build: {
ssr: !0,
emptyOutDir: !1,
outDir: `dist/${subFolder}`,
copyPublicDir: !1,
minify: !1,
rollupOptions: {
treeshake: treeshake ?? {
moduleSideEffects: !1
},
plugins: [
// otherwise rollup is leaving commonjs-only top level imports...
outputFormat === "esm" ? import_vxrn.rollupRemoveUnusedImportsPlugin : null].filter(Boolean),
// too many issues
// treeshake: {
// moduleSideEffects: false,
// },
// prevents it from shaking out the exports
preserveEntrySignatures: "strict",
input,
external: id => !1,
output: {
entryFileNames: "[name]",
exports: "auto",
...(outputFormat === "esm" ? {
format: "esm",
esModule: !0
} : {
format: "cjs",
// Preserve folder structure and use .cjs extension
entryFileNames: chunkInfo => chunkInfo.name.replace(/\.js$/, ".cjs"),
chunkFileNames: chunkInfo => {
const dir = import_node_path.default.dirname(chunkInfo.name),
name = import_node_path.default.basename(chunkInfo.name, import_node_path.default.extname(chunkInfo.name));
return import_node_path.default.join(dir, `${name}-[hash].cjs`);
},
assetFileNames: assetInfo => {
const name = assetInfo.name ?? "",
dir = import_node_path.default.dirname(name),
baseName = import_node_path.default.basename(name, import_node_path.default.extname(name)),
ext = import_node_path.default.extname(name);
return import_node_path.default.join(dir, `${baseName}-[hash]${ext}`);
}
})
}
}
}
}),
userApiBuildConf = oneOptions.build?.api?.config,
finalApiBuildConf = userApiBuildConf ? (0, import_vite.mergeConfig)(mergedConfig, userApiBuildConf) : mergedConfig;
return await (0, import_vite.build)(
// allow user merging api build config
finalApiBuildConf);
}
const builtMiddlewares = {},
apiPromise = manifest.apiRoutes.length ? (console.info(`
\u{1F528} build api routes
`), buildCustomRoutes("api", manifest.apiRoutes)) : Promise.resolve(null),
middlewarePromise = manifest.middlewareRoutes.length ? (console.info(`
\u{1F528} build middlewares
`), buildCustomRoutes("middlewares", manifest.middlewareRoutes)) : Promise.resolve(null),
[apiOutput, middlewareBuildInfo] = await Promise.all([apiPromise, middlewarePromise]);
if (middlewareBuildInfo) for (const middleware of manifest.middlewareRoutes) {
const absoluteRoot = (0, import_node_path.resolve)(process.cwd(), options.root),
fullPath = (0, import_node_path.join)(absoluteRoot, routerRoot, middleware.file),
chunk = middlewareBuildInfo.output.filter(x => x.type === "chunk").find(x => x.facadeModuleId === fullPath);
if (!chunk) throw new Error("internal err finding middleware");
builtMiddlewares[middleware.file] = (0, import_node_path.join)("dist", "middlewares", chunk.fileName);
}
globalThis.require = (0, import_node_module.createRequire)((0, import_node_path.join)(import_meta.url, ".."));
const assets = [],
builtRoutes = [],
sitemapData = [],
collectImportsCache = /* @__PURE__ */new Map(),
cssFileContentsCache = /* @__PURE__ */new Map(),
limit = (0, import_pLimit.pLimit)(BUILD_CONCURRENCY),
useWorkers = shouldUseWorkers(oneOptions),
workerPool = useWorkers ? (0, import_workerPool.getWorkerPool)(BUILD_CONCURRENCY) : null;
workerPool && (await workerPool.initialize());
const staticStartTime = performance.now(),
modeLabel = useWorkers ? `workers: ${workerPool?.size}` : `concurrency: ${BUILD_CONCURRENCY}`;
console.info(`
\u{1F528} build static routes (${modeLabel})
`);
const staticDir = (0, import_node_path.join)("dist/static"),
clientDir = (0, import_node_path.join)("dist/client");
if (await ensureDir(staticDir), !vxrnOutput.serverOutput) throw new Error("No server output");
const clientChunksBySource = /* @__PURE__ */new Map();
if (vxrnOutput.clientOutput) for (const chunk of vxrnOutput.clientOutput) chunk.type === "chunk" && chunk.facadeModuleId && clientChunksBySource.set(chunk.facadeModuleId, {
fileName: chunk.fileName,
imports: chunk.imports || []
});
const outputEntries = [...vxrnOutput.serverOutput.entries()],
layoutServerPaths = /* @__PURE__ */new Map();
for (const [, output] of outputEntries) {
if (output.type === "asset") continue;
const id = output.facadeModuleId || "";
if (import_node_path.default.basename(id).startsWith("_layout") && id.includes(`/${routerRoot}/`)) {
const contextKey = `./${(0, import_node_path.relative)(process.cwd(), id).replace(`${routerRoot}/`, "")}`;
layoutServerPaths.set(contextKey, output.fileName);
}
}
const routeByPath = /* @__PURE__ */new Map();
for (const route of manifest.pageRoutes) if (route.file) {
const routePath = `${routerRoot}${route.file.slice(1)}`;
routeByPath.set(routePath, route);
}
for (const [index, output] of outputEntries) {
let collectImports = function (entry, {
type = "js"
} = {}) {
const {
imports = [],
css
} = entry,
cacheKey = `${entry.file || imports.join(",")}:${type}`,
cached = collectImportsCache.get(cacheKey);
if (cached) return cached;
const result = [...new Set([...(type === "js" ? imports : css || []), ...imports.flatMap(name => {
const found = vxrnOutput.clientManifest[name];
return found || console.warn("No found imports", name, vxrnOutput.clientManifest), collectImports(found, {
type
});
})].flat().filter(x => x && (type === "css" || x.endsWith(".js"))).map(x => type === "css" || x.startsWith("assets/") ? x : `assets/${x.slice(1)}`))];
return collectImportsCache.set(cacheKey, result), result;
};
if (output.type === "asset") {
assets.push(output);
continue;
}
const id = output.facadeModuleId || "",
file = import_node_path.default.basename(id);
if (!id || file[0] === "_" || file.includes("entry-server") || id.includes("+api") || !id.includes(`/${routerRoot}/`)) continue;
const relativeId = (0, import_node_path.relative)(process.cwd(), id).replace(`${routerRoot}/`, "/"),
onlyBuild = vxrnOutput.buildArgs?.only;
if (onlyBuild && !import_micromatch.default.contains(relativeId, onlyBuild)) continue;
let foundRoute;
for (const [routePath, route] of routeByPath) if (id.endsWith(routePath)) {
foundRoute = route;
break;
}
if (!foundRoute) continue;
const clientChunk = clientChunksBySource.get(id),
manifestKey = `${routerRoot}${foundRoute.file.slice(1)}`,
clientManifestEntry = vxrnOutput.clientManifest[manifestKey];
if (!clientChunk && foundRoute.type !== "spa" && foundRoute.type !== "ssg") {
console.warn(`No client chunk found for route: ${id}`);
continue;
}
if (foundRoute.loaderServerPath = output.fileName, foundRoute.layouts) for (const layout of foundRoute.layouts) {
const serverPath = layoutServerPaths.get(layout.contextKey);
serverPath && (layout.loaderServerPath = serverPath);
}
const entryImports = collectImports(clientManifestEntry || {}),
layoutEntries = foundRoute.layouts?.flatMap(layout => {
const clientKey = `${routerRoot}${layout.contextKey.slice(1)}`,
found = vxrnOutput.clientManifest[clientKey];
return found || [];
}) ?? [],
layoutImports = layoutEntries.flatMap(entry => [entry.file, ...collectImports(entry)]),
routePreloads = {},
rootLayoutKey = `${routerRoot}/_layout.tsx`,
rootLayoutEntry = vxrnOutput.clientManifest[rootLayoutKey];
if (rootLayoutEntry && (routePreloads[`/${rootLayoutKey}`] = `/${rootLayoutEntry.file}`), foundRoute.layouts) for (const layout of foundRoute.layouts) {
const clientKey = `${routerRoot}${layout.contextKey.slice(1)}`,
entry = vxrnOutput.clientManifest[clientKey];
entry && (routePreloads[`/${clientKey}`] = `/${entry.file}`);
}
if (clientChunk) {
const routeKey = `/${routerRoot}${foundRoute.file.slice(1)}`;
routePreloads[routeKey] = `/${clientChunk.fileName}`;
} else if (clientManifestEntry) {
const routeKey = `/${routerRoot}${foundRoute.file.slice(1)}`;
routePreloads[routeKey] = `/${clientManifestEntry.file}`;
}
const preloadSetupFilePreloads = (() => {
if (!oneOptions.setupFile) return [];
const clientSetupFile = typeof oneOptions.setupFile == "string" ? oneOptions.setupFile : oneOptions.setupFile.client;
if (!clientSetupFile) return [];
const needle = clientSetupFile.replace(/^\.\//, "");
for (const file2 in vxrnOutput.clientManifest) if (file2 === needle) return [vxrnOutput.clientManifest[file2].file
// getting 404s for preloading the imports as well?
// ...(entry.imports as string[])
];
return [];
})(),
allPreloads = [... /* @__PURE__ */new Set([...preloadSetupFilePreloads,
// add the route entry js (like ./app/index.ts) - prefer direct chunk lookup
...(clientChunk ? [clientChunk.fileName] : clientManifestEntry ? [clientManifestEntry.file] : []),
// add the virtual entry
vxrnOutput.clientManifest["virtual:one-entry"].file, ...entryImports, ...layoutImports])].map(path => `/${path}`),
scriptLoadingMode = oneOptions.web?.experimental_scriptLoading,
needsSeparatedPreloads = scriptLoadingMode === "defer-non-critical" || scriptLoadingMode === "after-lcp-aggressive",
criticalPreloads = needsSeparatedPreloads ? [... /* @__PURE__ */new Set([...preloadSetupFilePreloads,
// add the virtual entry (framework bootstrap)
vxrnOutput.clientManifest["virtual:one-entry"].file,
// add the route entry js (like ./app/index.ts) - prefer direct chunk lookup
...(clientChunk ? [clientChunk.fileName] : clientManifestEntry ? [clientManifestEntry.file] : []),
// add layout files (but not their deep imports)
...layoutEntries.map(entry => entry.file)])].map(path => `/${path}`) : void 0,
deferredPreloads = needsSeparatedPreloads ? [... /* @__PURE__ */new Set([...entryImports, ...layoutEntries.flatMap(entry => collectImports(entry))])].filter(path => !criticalPreloads.includes(`/${path}`)).map(path => `/${path}`) : void 0,
preloads2 = needsSeparatedPreloads ? [...criticalPreloads, ...deferredPreloads] : allPreloads,
allEntries = [clientManifestEntry, ...layoutEntries].filter(Boolean),
allCSS = [... /* @__PURE__ */new Set([
// css from entry imports
...allEntries.flatMap(entry => collectImports(entry, {
type: "css"
})).map(path => `/${path}`),
// root-level css (handles cssCodeSplit: false)
...Object.entries(vxrnOutput.clientManifest).filter(([key]) => key.endsWith(".css")).map(([, entry]) => `/${entry.file}`)])];
let allCSSContents;
oneOptions.web?.inlineLayoutCSS && (allCSSContents = await Promise.all(allCSS.map(async cssPath => {
const cached = cssFileContentsCache.get(cssPath);
if (cached !== void 0) return cached;
const filePath = (0, import_node_path.join)(clientDir, cssPath);
try {
const content = await import_fs_extra.default.readFile(filePath, "utf-8");
return cssFileContentsCache.set(cssPath, content), content;
} catch {
return console.warn(`[one] Warning: Could not read CSS file ${filePath}`), cssFileContentsCache.set(cssPath, ""), "";
}
}))), process.env.DEBUG && console.info("[one] building routes", {
foundRoute,
layoutEntries,
allEntries,
allCSS
});
const serverJsPath = (0, import_node_path.join)("dist/server", output.fileName);
let exported;
try {
exported = await import((0, import_toAbsolute.toAbsolute)(serverJsPath));
} catch (err) {
throw console.error("Error importing page (original error)", err), new Error(`Error importing page: ${serverJsPath}`, {
cause: err
});
}
const isDynamic = !!Object.keys(foundRoute.routeKeys).length;
if (foundRoute.type === "ssg" && isDynamic && !foundRoute.page.includes("+not-found") && !foundRoute.page.includes("_sitemap") && !exported.generateStaticParams) throw new Error(`[one] Error: Missing generateStaticParams
Route ${foundRoute.page} of type ${foundRoute.type} must export generateStaticParams so build can complete.
See docs on generateStaticParams:
https://onestack.dev/docs/routing-exports#generatestaticparams
`);
const paramsList = (await exported.generateStaticParams?.()) ?? [{}];
console.info(`
[build] page ${relativeId} (with ${paramsList.length} routes)
`), process.env.DEBUG && console.info("paramsList", JSON.stringify(paramsList, null, 2));
const routeSitemapExport = exported.sitemap,
isAfterLCPMode = scriptLoadingMode === "after-lcp" || scriptLoadingMode === "after-lcp-aggressive",
useAfterLCP = foundRoute.type === "ssg" && isAfterLCPMode,
useAfterLCPAggressive = foundRoute.type === "ssg" && scriptLoadingMode === "after-lcp-aggressive",
shouldCollectSitemap = foundRoute.type !== "api" && foundRoute.type !== "layout" && !foundRoute.isNotFound && !foundRoute.page.includes("+not-found") && !foundRoute.page.includes("_sitemap"),
pageBuilds = paramsList.map(params => {
const path = (0, import_getPathnameFromFilePath.getPathnameFromFilePath)(relativeId, params, foundRoute.type === "ssg");
return workerPool ? (console.info(` \u21A6 route ${path}`), workerPool.buildPage({
serverEntry: vxrnOutput.serverEntry,
path,
relativeId,
params,
foundRoute,
clientManifestEntry,
staticDir,
clientDir,
builtMiddlewares,
serverJsPath,
preloads: preloads2,
allCSS,
routePreloads,
allCSSContents,
criticalPreloads,
deferredPreloads,
useAfterLCP,
useAfterLCPAggressive
}).then(built => ({
built,
path
}))) : limit(async () => (console.info(` \u21A6 route ${path}`), {
built: await (0, import_one_server_only.runWithAsyncLocalContext)(async () => await (0, import_buildPage.buildPage)(vxrnOutput.serverEntry, path, relativeId, params, foundRoute, clientManifestEntry, staticDir, clientDir, builtMiddlewares, serverJsPath, preloads2, allCSS, routePreloads, allCSSContents, criticalPreloads, deferredPreloads, useAfterLCP, useAfterLCPAggressive)),
path
}));
}),
results = await Promise.all(pageBuilds);
for (const {
built,
path
} of results) builtRoutes.push(built), shouldCollectSitemap && sitemapData.push({
path,
routeExport: routeSitemapExport
});
}
workerPool && (await (0, import_workerPool.terminateWorkerPool)());
const staticTime = performance.now() - staticStartTime;
console.info(`
\u23F1\uFE0F static routes: ${(staticTime / 1e3).toFixed(2)}s (${builtRoutes.length} pages)
`), (0, import_buildPage.printBuildTimings)(), await moveAllFiles(staticDir, clientDir), await import_fs_extra.default.rm(staticDir, {
force: !0,
recursive: !0
});
const routeMap = {},
routeToBuildInfo = {},
pathToRoute = {},
preloads = {},
cssPreloads = {},
loaders = {};
for (const route of builtRoutes) {
route.cleanPath.includes("*") || (routeMap[route.cleanPath] = route.htmlPath);
const {
// dont include loaderData it can be huge
loaderData: _loaderData,
...rest
} = route;
routeToBuildInfo[route.routeFile] = rest;
for (const p of getCleanPaths([route.path, route.cleanPath])) pathToRoute[p] = route.routeFile;
preloads[route.preloadPath] = !0, cssPreloads[route.cssPreloadPath] = !0, loaders[route.loaderPath] = !0;
}
function createBuildManifestRoute(route) {
const {
layouts,
...built
} = route;
layouts?.length && (built.layouts = layouts.map(layout => ({
contextKey: layout.contextKey,
loaderServerPath: layout.loaderServerPath
})));
const buildInfo = builtRoutes.find(x => x.routeFile === route.file);
if (built.middlewares && buildInfo?.middlewares) for (const [index, mw] of built.middlewares.entries()) mw.contextKey = buildInfo.middlewares[index];
return buildInfo && (built.loaderPath = buildInfo.loaderPath), built;
}
const buildInfoForWriting = {
oneOptions,
routeToBuildInfo,
pathToRoute,
manifest: {
pageRoutes: manifest.pageRoutes.map(createBuildManifestRoute),
apiRoutes: manifest.apiRoutes.map(createBuildManifestRoute),
allRoutes: manifest.allRoutes.map(createBuildManifestRoute)
},
routeMap,
constants: JSON.parse(JSON.stringify({
...constants
})),
preloads,
cssPreloads,
loaders,
useRolldown: await (0, import_isRolldown.isRolldown)()
};
await writeJSON((0, import_toAbsolute.toAbsolute)("dist/buildInfo.json"), buildInfoForWriting);
const sitemapConfig = oneOptions.web?.sitemap;
if (sitemapConfig) {
const sitemapXml = (0, import_generateSitemap.generateSitemap)(sitemapData, typeof sitemapConfig == "boolean" ? {} : sitemapConfig),
sitemapPath = (0, import_node_path.join)(clientDir, "sitemap.xml");
await import_fs_extra.default.writeFile(sitemapPath, sitemapXml), console.info(`
\u{1F4C4} generated sitemap.xml (${sitemapData.length} URLs)
`);
}
const postBuildLogs = [],
platform = oneOptions.web?.deploy;
switch (platform && postBuildLogs.push(`[one.build] platform ${platform}`), platform) {
case "vercel":
{
const vercelJsonPath = (0, import_node_path.join)(options.root, "vercel.json");
if (import_fs_extra.default.existsSync(vercelJsonPath)) try {
JSON.parse(import_fs_extra.default.readFileSync(vercelJsonPath, "utf-8")).cleanUrls || (console.warn(`
\u26A0\uFE0F Warning: Your vercel.json is missing "cleanUrls": true`), console.warn(" Without this, direct navigation to SSG pages will 404."), console.warn(` Add "cleanUrls": true to your vercel.json to fix this.
`));
} catch {}
await (0, import_buildVercelOutputDirectory.buildVercelOutputDirectory)({
apiOutput,
buildInfoForWriting,
clientDir,
oneOptionsRoot: options.root,
postBuildLogs
});
break;
}
case "cloudflare":
{
const pageRouteMap = [],
apiRouteMap = [],
middlewareRouteMap = [];
for (const [routeFile, info] of Object.entries(buildInfoForWriting.routeToBuildInfo)) if (info.serverJsPath) {
const importPath = "./" + info.serverJsPath.replace(/^dist\//, "");
pageRouteMap.push(` '${routeFile}': () => import('${importPath}')`);
}
for (const route of buildInfoForWriting.manifest.apiRoutes) if (route.file) {
const importPath = `./api/${route.page.slice(1).replace(/\[/g, "_").replace(/\]/g, "_")}.js`;
apiRouteMap.push(` '${route.page}': () => import('${importPath}')`);
}
for (const [middlewareFile, builtPath] of Object.entries(builtMiddlewares)) {
const importPath = "./" + builtPath.replace(/^dist\//, "");
middlewareRouteMap.push(` '${builtPath}': () => import('${importPath}')`);
}
const workerSrcPath = (0, import_node_path.join)(options.root, "dist", "_worker-src.js"),
workerCode = `// Polyfill MessageChannel for React SSR (not available in Cloudflare Workers by default)
if (typeof MessageChannel === 'undefined') {
globalThis.MessageChannel = class MessageChannel {
constructor() {
this.port1 = { postMessage: () => {}, onmessage: null, close: () => {} }
this.port2 = { postMessage: () => {}, onmessage: null, close: () => {} }
}
}
}
import { serve, setFetchStaticHtml } from 'one/serve-worker'
// Lazy import map - modules load on-demand when route is matched
const lazyRoutes = {
serverEntry: () => import('./server/_virtual_one-entry.js'),
pages: {
${pageRouteMap.join(`,
`)}
},
api: {
${apiRouteMap.join(`,
`)}
},
middlewares: {
${middlewareRouteMap.join(`,
`)}
}
}
const buildInfo = ${JSON.stringify(buildInfoForWriting)}
let app
export default {
async fetch(request, env, ctx) {
if (!app) {
app = await serve(buildInfo, lazyRoutes)
}
// Set up static HTML fetcher for this request (uses ASSETS binding)
if (env.ASSETS) {
setFetchStaticHtml(async (path) => {
try {
const url = new URL(request.url)
url.pathname = path
const assetResponse = await env.ASSETS.fetch(new Request(url))
if (assetResponse && assetResponse.ok) {
return await assetResponse.text()
}
} catch (e) {
// Asset not found
}
return null
})
}
try {
// Try the app first
const response = await app.fetch(request, env, ctx)
// If no route matched (404) or no response, try serving static assets
if (!response || response.status === 404) {
if (env.ASSETS) {
try {
const assetResponse = await env.ASSETS.fetch(request)
// If asset exists, return it
if (assetResponse && assetResponse.status !== 404) {
return assetResponse
}
} catch (e) {
// Asset not found, continue with original response
}
}
}
return response
} finally {
// Clean up per-request state
setFetchStaticHtml(null)
}
}
}
`;
await import_fs_extra.default.writeFile(workerSrcPath, workerCode), console.info(`
[cloudflare] Bundling worker...`), await (0, import_vite.build)({
root: options.root,
logLevel: "warn",
build: {
outDir: "dist",
emptyOutDir: !1,
// Use SSR mode with node target for proper Node.js module resolution
ssr: workerSrcPath,
rollupOptions: {
external: [
// React Native dev tools - not needed in production
"@react-native/dev-middleware", "@react-native/debugger-shell", "metro", "metro-core", "metro-runtime",
// Native modules that can't run in workers
/\.node$/],
output: {
entryFileNames: "worker.js",
format: "es",
// Keep dynamic imports separate for lazy loading
inlineDynamicImports: !1
}
},
minify: !0,
target: "esnext"
},
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
"process.env.VITE_ENVIRONMENT": JSON.stringify("ssr")
},
resolve: {
conditions: ["workerd", "worker", "node", "module", "default"]
},
ssr: {
target: "node",
noExternal: !0
}
}), await import_fs_extra.default.remove(workerSrcPath), await import_fs_extra.default.writeFile((0, import_node_path.join)(options.root, "dist", "wrangler.jsonc"), `{
"name": "one-app",
"main": "worker.js",
"compatibility_date": "2024-12-05",
"compatibility_flags": ["nodejs_compat"],
"find_additional_modules": true,
"rules": [
{ "type": "ESModule", "globs": ["./server/**/*.js"], "fallthrough": true },
{ "type": "ESModule", "globs": ["./api/**/*.js"], "fallthrough": true },
{ "type": "ESModule", "globs": ["./middlewares/**/*.js"], "fallthrough": true }
],
"assets": { "directory": "client", "binding": "ASSETS", "run_worker_first": true }
}
`), postBuildLogs.push("Cloudflare worker bundled at dist/worker.js"), postBuildLogs.push("To deploy: cd dist && wrangler deploy");
break;
}
}
postBuildLogs.length && (console.info(`
`), postBuildLogs.forEach(log => {
console.info(` \xB7 ${log}`);
})), console.info(`
\u{1F49B} build complete
`);
}
const TRAILING_INDEX_REGEX = /\/index(\.(web))?/;
function getCleanPaths(possiblePaths) {
return Array.from(new Set(Array.from(new Set(possiblePaths)).flatMap(p => {
const paths = [p];
if (p.match(TRAILING_INDEX_REGEX)) {
const pathWithTrailingIndexRemoved = p.replace(TRAILING_INDEX_REGEX, "");
paths.push(pathWithTrailingIndexRemoved), paths.push(pathWithTrailingIndexRemoved + "/");
}
return paths;
})));
}
async function moveAllFiles(src, dest) {
try {
await import_fs_extra.default.copy(src, dest, {
overwrite: !0,
errorOnExist: !1
});
} catch (err) {
console.error("Error moving files:", err);
}
}