one
Version:
One is a new React Framework that makes Vite serve both native and web.
340 lines (333 loc) • 16.4 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_fs_extra = __toESM(require("fs-extra"), 1), import_micromatch = __toESM(require("micromatch"), 1), import_node_module = require("node:module"), import_node_path = __toESM(require("node:path"), 1), import_vite = require("vite"), import_vxrn = require("vxrn"), constants = __toESM(require("../constants"), 1), import_setServerGlobals = require("../server/setServerGlobals"), import_toAbsolute = require("../utils/toAbsolute"), import_getManifest = require("../vite/getManifest"), import_loadConfig = require("../vite/loadConfig"), import_one_server_only = require("../vite/one-server-only"), import_buildVercelOutputDirectory = require("../vercel/build/buildVercelOutputDirectory"), import_getRouterRootFromOneOptions = require("../utils/getRouterRootFromOneOptions"), import_buildPage = require("./buildPage"), import_checkNodeVersion = require("./checkNodeVersion"), import_label_process = require("./label-process"), import_getPathnameFromFilePath = require("../utils/getPathnameFromFilePath");
const import_meta = {}, { ensureDir, writeJSON } = import_fs_extra.default;
process.on("uncaughtException", (err) => {
console.error(err?.message || err);
});
async function build(args) {
process.env.IS_VXRN_CLI = "true", 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), routerRootRegexp = new RegExp(`^${routerRoot}`), manifest = (0, import_getManifest.getManifest)({ routerRoot }), serverOutputFormat = oneOptions.build?.server === !1 ? "esm" : oneOptions.build?.server?.outputFormat ?? "esm", vxrnOutput = await (0, import_vxrn.build)(
{
server: oneOptions.server,
build: {
analyze: !0,
server: oneOptions.build?.server === !1 ? !1 : {
outputFormat: serverOutputFormat
}
}
},
args
);
if (!vxrnOutput || args.platform !== "web")
return;
const options = await (0, import_vxrn.fillOptions)(vxrnOutput.options), { 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
);
}
let apiOutput = null;
manifest.apiRoutes.length && (console.info(`
\u{1F528} build api routes
`), apiOutput = await buildCustomRoutes("api", manifest.apiRoutes));
const builtMiddlewares = {};
if (manifest.middlewareRoutes.length) {
console.info(`
\u{1F528} build middlewares
`);
const middlewareBuildInfo = await buildCustomRoutes("middlewares", manifest.middlewareRoutes);
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 = [];
console.info(`
\u{1F528} build static routes
`);
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 outputEntries = [...vxrnOutput.serverOutput.entries()];
for (const [index, output] of outputEntries) {
let collectImports = function({ imports = [], css }, { type = "js" } = {}) {
return [
...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)}`)
)
];
};
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;
const clientManifestKey = Object.keys(vxrnOutput.clientManifest).find((key) => id.endsWith(key)) || "";
if (!clientManifestKey)
continue;
const clientManifestEntry = vxrnOutput.clientManifest[clientManifestKey], foundRoute = manifest.pageRoutes.find((route) => route.file && clientManifestKey.replace(routerRootRegexp, "") === route.file.slice(1));
if (!foundRoute)
continue;
foundRoute.loaderServerPath = output.fileName, clientManifestEntry || console.warn(
`No client manifest entry found: ${clientManifestKey} in manifest ${JSON.stringify(
vxrnOutput.clientManifest,
null,
2
)}`
);
const entryImports = collectImports(clientManifestEntry || {}), layoutEntries = foundRoute.layouts?.flatMap((layout) => {
const clientKey = `${routerRoot}${layout.contextKey.slice(1)}`;
return vxrnOutput.clientManifest[clientKey];
}) ?? [], layoutImports = layoutEntries.flatMap((entry) => [entry.file, ...collectImports(entry)]), preloadSetupFilePreloads = (() => {
if (oneOptions.setupFile) {
const needle = oneOptions.setupFile.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 [];
})(), preloads2 = [
.../* @__PURE__ */ new Set([
...preloadSetupFilePreloads,
// add the route entry js (like ./app/index.ts)
clientManifestEntry.file,
// add the virtual entry
vxrnOutput.clientManifest["virtual:one-entry"].file,
...entryImports,
...layoutImports
])
].map((path) => `/${path}`), allEntries = [clientManifestEntry, ...layoutEntries], allCSS = allEntries.flatMap((entry) => collectImports(entry, { type: "css" })).map((path) => `/${path}`);
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));
for (const params of paramsList) {
const path = (0, import_getPathnameFromFilePath.getPathnameFromFilePath)(relativeId, params, foundRoute.type === "ssg");
console.info(` \u21A6 route ${path}`);
const 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
));
builtRoutes.push(built);
}
}
await moveAllFiles(staticDir, clientDir), await import_fs_extra.default.rm(staticDir, { force: !0, recursive: !0 });
const routeMap = {}, routeToBuildInfo = {}, pathToRoute = {}, preloads = {}, 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 (let p of getCleanPaths([route.path, route.cleanPath]))
pathToRoute[p] = route.routeFile;
preloads[route.preloadPath] = !0, loaders[route.loaderPath] = !0;
}
function createBuildManifestRoute(route) {
const { layouts, ...built } = route, 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,
loaders
};
await writeJSON((0, import_toAbsolute.toAbsolute)("dist/buildInfo.json"), buildInfoForWriting);
let postBuildLogs = [];
const platform = oneOptions.web?.deploy;
switch (platform && postBuildLogs.push(`[one.build] platform ${platform}`), platform) {
case "vercel": {
await (0, import_buildVercelOutputDirectory.buildVercelOutputDirectory)({
apiOutput,
buildInfoForWriting,
clientDir,
oneOptionsRoot: options.root,
postBuildLogs
});
break;
}
}
process.env.VXRN_ANALYZE_BUNDLE && postBuildLogs.push(`client build report: ${(0, import_toAbsolute.toAbsolute)("dist/report.html")}`), 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);
}
}
//# sourceMappingURL=build.js.map