one
Version:
One is a new React Framework that makes Vite serve both native and web.
486 lines (479 loc) • 21.6 kB
JavaScript
import FSExtra from "fs-extra";
import MicroMatch from "micromatch";
import { createRequire } from "node:module";
import Path, { join, relative, resolve } from "node:path";
import { mergeConfig, build as viteBuild } from "vite";
import { fillOptions, getOptimizeDeps, rollupRemoveUnusedImportsPlugin, build as vxrnBuild } from "vxrn";
import * as constants from "../constants.native.js";
import { setServerGlobals } from "../server/setServerGlobals.native.js";
import { toAbsolute } from "../utils/toAbsolute.native.js";
import { getManifest } from "../vite/getManifest.native.js";
import { loadUserOneOptions } from "../vite/loadConfig.native.js";
import { runWithAsyncLocalContext } from "../vite/one-server-only.native.js";
import { buildVercelOutputDirectory } from "../vercel/build/buildVercelOutputDirectory.native.js";
import { getRouterRootFromOneOptions } from "../utils/getRouterRootFromOneOptions.native.js";
import { buildPage } from "./buildPage.native.js";
import { checkNodeVersion } from "./checkNodeVersion.native.js";
import { labelProcess } from "./label-process.native.js";
import { getPathnameFromFilePath } from "../utils/getPathnameFromFilePath.native.js";
var {
ensureDir,
writeJSON
} = FSExtra;
process.on("uncaughtException", function (err) {
console.error(err?.message || err);
});
async function build(args) {
var _oneOptions_build, _oneOptions_build_server, _oneOptions_build1, _oneOptions_build2, _oneOptions_web;
process.env.IS_VXRN_CLI = "true", process.env.NODE_ENV = "production", labelProcess("build"), checkNodeVersion(), setServerGlobals();
var {
oneOptions
} = await loadUserOneOptions("build"),
routerRoot = getRouterRootFromOneOptions(oneOptions),
routerRootRegexp = new RegExp(`^${routerRoot}`),
manifest = getManifest({
routerRoot
}),
_oneOptions_build_server_outputFormat,
serverOutputFormat = ((_oneOptions_build = oneOptions.build) === null || _oneOptions_build === void 0 ? void 0 : _oneOptions_build.server) === !1 ? "esm" : (_oneOptions_build_server_outputFormat = (_oneOptions_build1 = oneOptions.build) === null || _oneOptions_build1 === void 0 || (_oneOptions_build_server = _oneOptions_build1.server) === null || _oneOptions_build_server === void 0 ? void 0 : _oneOptions_build_server.outputFormat) !== null && _oneOptions_build_server_outputFormat !== void 0 ? _oneOptions_build_server_outputFormat : "esm",
vxrnOutput = await vxrnBuild({
server: oneOptions.server,
build: {
analyze: !0,
server: ((_oneOptions_build2 = oneOptions.build) === null || _oneOptions_build2 === void 0 ? void 0 : _oneOptions_build2.server) === !1 ? !1 : {
outputFormat: serverOutputFormat
}
}
}, args);
if (!vxrnOutput || args.platform !== "web") return;
var options = await fillOptions(vxrnOutput.options),
{
optimizeDeps
} = getOptimizeDeps("build"),
apiBuildConfig = 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) {
var _oneOptions_build_api,
_oneOptions_build3,
_oneOptions_build_api1,
_oneOptions_build12,
_oneOptions_build_api2,
_oneOptions_build22,
input = routes.reduce(function (entries, param) {
var {
page,
file
} = param;
return entries[page.slice(1) + ".js"] = join(routerRoot, file), entries;
}, {}),
_oneOptions_build_api_outputFormat,
outputFormat = (_oneOptions_build_api_outputFormat = oneOptions == null || (_oneOptions_build3 = oneOptions.build) === null || _oneOptions_build3 === void 0 || (_oneOptions_build_api = _oneOptions_build3.api) === null || _oneOptions_build_api === void 0 ? void 0 : _oneOptions_build_api.outputFormat) !== null && _oneOptions_build_api_outputFormat !== void 0 ? _oneOptions_build_api_outputFormat : serverOutputFormat,
treeshake = oneOptions == null || (_oneOptions_build12 = oneOptions.build) === null || _oneOptions_build12 === void 0 || (_oneOptions_build_api1 = _oneOptions_build12.api) === null || _oneOptions_build_api1 === void 0 ? void 0 : _oneOptions_build_api1.treeshake,
mergedConfig = 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" ? rollupRemoveUnusedImportsPlugin : null].filter(Boolean),
// too many issues
// treeshake: {
// moduleSideEffects: false,
// },
// prevents it from shaking out the exports
preserveEntrySignatures: "strict",
input,
external: function (id) {
return !1;
},
output: {
entryFileNames: "[name]",
exports: "auto",
...(outputFormat === "esm" ? {
format: "esm",
esModule: !0
} : {
format: "cjs",
// Preserve folder structure and use .cjs extension
entryFileNames: function (chunkInfo) {
var name = chunkInfo.name.replace(/\.js$/, ".cjs");
return name;
},
chunkFileNames: function (chunkInfo) {
var dir = Path.dirname(chunkInfo.name),
name = Path.basename(chunkInfo.name, Path.extname(chunkInfo.name));
return Path.join(dir, `${name}-[hash].cjs`);
},
assetFileNames: function (assetInfo) {
var _assetInfo_name,
name = (_assetInfo_name = assetInfo.name) !== null && _assetInfo_name !== void 0 ? _assetInfo_name : "",
dir = Path.dirname(name),
baseName = Path.basename(name, Path.extname(name)),
ext = Path.extname(name);
return Path.join(dir, `${baseName}-[hash]${ext}`);
}
})
}
}
}
}),
userApiBuildConf = (_oneOptions_build22 = oneOptions.build) === null || _oneOptions_build22 === void 0 || (_oneOptions_build_api2 = _oneOptions_build22.api) === null || _oneOptions_build_api2 === void 0 ? void 0 : _oneOptions_build_api2.config,
finalApiBuildConf = userApiBuildConf ? mergeConfig(mergedConfig, userApiBuildConf) : mergedConfig,
output = await viteBuild(
// allow user merging api build config
finalApiBuildConf);
return output;
}
var apiOutput = null;
manifest.apiRoutes.length && (console.info(`
\u{1F528} build api routes
`), apiOutput = await buildCustomRoutes("api", manifest.apiRoutes));
var builtMiddlewares = {};
if (manifest.middlewareRoutes.length) {
console.info(`
\u{1F528} build middlewares
`);
var middlewareBuildInfo = await buildCustomRoutes("middlewares", manifest.middlewareRoutes),
_iteratorNormalCompletion = !0,
_didIteratorError = !1,
_iteratorError = void 0;
try {
for (var _loop = function () {
var middleware = _step.value,
absoluteRoot = resolve(process.cwd(), options.root),
fullPath = join(absoluteRoot, routerRoot, middleware.file),
outChunks = middlewareBuildInfo.output.filter(function (x) {
return x.type === "chunk";
}),
chunk = outChunks.find(function (x) {
return x.facadeModuleId === fullPath;
});
if (!chunk) throw new Error("internal err finding middleware");
builtMiddlewares[middleware.file] = join("dist", "middlewares", chunk.fileName);
}, _iterator = manifest.middlewareRoutes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = !0) _loop();
} catch (err) {
_didIteratorError = !0, _iteratorError = err;
} finally {
try {
!_iteratorNormalCompletion && _iterator.return != null && _iterator.return();
} finally {
if (_didIteratorError) throw _iteratorError;
}
}
}
globalThis.require = createRequire(join(import.meta.url, ".."));
var assets = [],
builtRoutes = [];
console.info(`
\u{1F528} build static routes
`);
var staticDir = join("dist/static"),
clientDir = join("dist/client");
if (await ensureDir(staticDir), !vxrnOutput.serverOutput) throw new Error("No server output");
var outputEntries = [...vxrnOutput.serverOutput.entries()],
_iteratorNormalCompletion1 = !0,
_didIteratorError1 = !1,
_iteratorError1 = void 0;
try {
for (var _loop1 = async function () {
var [index, output] = _step1.value,
_vxrnOutput_buildArgs,
_foundRoute_layouts,
_exported_generateStaticParams;
if (output.type === "asset") return assets.push(output), "continue";
var id = output.facadeModuleId || "",
file = Path.basename(id);
if (!id || file[0] === "_" || file.includes("entry-server") || id.includes("+api") || !id.includes(`/${routerRoot}/`)) return "continue";
var relativeId = relative(process.cwd(), id).replace(`${routerRoot}/`, "/"),
onlyBuild = (_vxrnOutput_buildArgs = vxrnOutput.buildArgs) === null || _vxrnOutput_buildArgs === void 0 ? void 0 : _vxrnOutput_buildArgs.only;
if (onlyBuild && !MicroMatch.contains(relativeId, onlyBuild)) return "continue";
var clientManifestKey = Object.keys(vxrnOutput.clientManifest).find(function (key) {
return id.endsWith(key);
}) || "";
if (!clientManifestKey) return "continue";
var clientManifestEntry = vxrnOutput.clientManifest[clientManifestKey],
foundRoute = manifest.pageRoutes.find(function (route2) {
return route2.file && clientManifestKey.replace(routerRootRegexp, "") === route2.file.slice(1);
});
if (!foundRoute) return "continue";
foundRoute.loaderServerPath = output.fileName;
function collectImports(param) {
var {
imports = [],
css
} = param,
{
type = "js"
} = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
return [...new Set([...(type === "js" ? imports : css || []), ...imports.flatMap(function (name) {
var found = vxrnOutput.clientManifest[name];
return found || console.warn("No found imports", name, vxrnOutput.clientManifest), collectImports(found, {
type
});
})].flat().filter(function (x) {
return x && (type === "css" || x.endsWith(".js"));
}).map(function (x) {
return type === "css" || x.startsWith("assets/") ? x : `assets/${x.slice(1)}`;
}))];
}
clientManifestEntry || console.warn(`No client manifest entry found: ${clientManifestKey} in manifest ${JSON.stringify(vxrnOutput.clientManifest, null, 2)}`);
var entryImports = collectImports(clientManifestEntry || {}),
_foundRoute_layouts_flatMap,
layoutEntries = (_foundRoute_layouts_flatMap = (_foundRoute_layouts = foundRoute.layouts) === null || _foundRoute_layouts === void 0 ? void 0 : _foundRoute_layouts.flatMap(function (layout) {
var clientKey = `${routerRoot}${layout.contextKey.slice(1)}`;
return vxrnOutput.clientManifest[clientKey];
})) !== null && _foundRoute_layouts_flatMap !== void 0 ? _foundRoute_layouts_flatMap : [],
layoutImports = layoutEntries.flatMap(function (entry) {
return [entry.file, ...collectImports(entry)];
}),
preloadSetupFilePreloads = function () {
if (oneOptions.setupFile) {
var needle = oneOptions.setupFile.replace(/^\.\//, "");
for (var file2 in vxrnOutput.clientManifest) if (file2 === needle) {
var entry = vxrnOutput.clientManifest[file2];
return [entry.file];
}
}
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(function (path) {
return `/${path}`;
}),
allEntries = [clientManifestEntry, ...layoutEntries],
allCSS = allEntries.flatMap(function (entry) {
return collectImports(entry, {
type: "css"
});
}).map(function (path) {
return `/${path}`;
});
process.env.DEBUG && console.info("[one] building routes", {
foundRoute,
layoutEntries,
allEntries,
allCSS
});
var serverJsPath = join("dist/server", output.fileName),
exported = void 0;
try {
exported = await import(toAbsolute(serverJsPath));
} catch (err) {
throw console.error("Error importing page (original error)", err), new Error(`Error importing page: ${serverJsPath}`, {
cause: err
});
}
var 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
`);
var _ref,
paramsList = (_ref = await ((_exported_generateStaticParams = exported.generateStaticParams) === null || _exported_generateStaticParams === void 0 ? void 0 : _exported_generateStaticParams.call(exported))) !== null && _ref !== void 0 ? _ref : [{}];
console.info(`
[build] page ${relativeId} (with ${paramsList.length} routes)
`), process.env.DEBUG && console.info("paramsList", JSON.stringify(paramsList, null, 2));
var _iteratorNormalCompletion4 = !0,
_didIteratorError4 = !1,
_iteratorError4 = void 0;
try {
for (var _loop2 = async function () {
var params = _step4.value,
path = getPathnameFromFilePath(relativeId, params, foundRoute.type === "ssg");
console.info(` \u21A6 route ${path}`);
var built = await runWithAsyncLocalContext(async function () {
return await buildPage(vxrnOutput.serverEntry, path, relativeId, params, foundRoute, clientManifestEntry, staticDir, clientDir, builtMiddlewares, serverJsPath, preloads2, allCSS);
});
builtRoutes.push(built);
}, _iterator4 = paramsList[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = !0) await _loop2();
} catch (err) {
_didIteratorError4 = !0, _iteratorError4 = err;
} finally {
try {
!_iteratorNormalCompletion4 && _iterator4.return != null && _iterator4.return();
} finally {
if (_didIteratorError4) throw _iteratorError4;
}
}
}, _iterator1 = outputEntries[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = !0) await _loop1();
} catch (err) {
_didIteratorError1 = !0, _iteratorError1 = err;
} finally {
try {
!_iteratorNormalCompletion1 && _iterator1.return != null && _iterator1.return();
} finally {
if (_didIteratorError1) throw _iteratorError1;
}
}
await moveAllFiles(staticDir, clientDir), await FSExtra.rm(staticDir, {
force: !0,
recursive: !0
});
var routeMap = {},
routeToBuildInfo = {},
pathToRoute = {},
preloads = {},
loaders = {},
_iteratorNormalCompletion2 = !0,
_didIteratorError2 = !1,
_iteratorError2 = void 0;
try {
for (var _iterator2 = builtRoutes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = !0) {
var route = _step2.value;
route.cleanPath.includes("*") || (routeMap[route.cleanPath] = route.htmlPath);
var {
// dont include loaderData it can be huge
loaderData: _loaderData,
...rest
} = route;
routeToBuildInfo[route.routeFile] = rest;
var _iteratorNormalCompletion3 = !0,
_didIteratorError3 = !1,
_iteratorError3 = void 0;
try {
for (var _iterator3 = getCleanPaths([route.path, route.cleanPath])[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = !0) {
var p = _step3.value;
pathToRoute[p] = route.routeFile;
}
} catch (err) {
_didIteratorError3 = !0, _iteratorError3 = err;
} finally {
try {
!_iteratorNormalCompletion3 && _iterator3.return != null && _iterator3.return();
} finally {
if (_didIteratorError3) throw _iteratorError3;
}
}
preloads[route.preloadPath] = !0, loaders[route.loaderPath] = !0;
}
} catch (err) {
_didIteratorError2 = !0, _iteratorError2 = err;
} finally {
try {
!_iteratorNormalCompletion2 && _iterator2.return != null && _iterator2.return();
} finally {
if (_didIteratorError2) throw _iteratorError2;
}
}
function createBuildManifestRoute(route2) {
var {
layouts,
...built
} = route2,
buildInfo = builtRoutes.find(function (x) {
return x.routeFile === route2.file;
});
if (built.middlewares && buildInfo?.middlewares) {
var _iteratorNormalCompletion4 = !0,
_didIteratorError4 = !1,
_iteratorError4 = void 0;
try {
for (var _iterator4 = built.middlewares.entries()[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = !0) {
var [index, mw] = _step4.value;
mw.contextKey = buildInfo.middlewares[index];
}
} catch (err) {
_didIteratorError4 = !0, _iteratorError4 = err;
} finally {
try {
!_iteratorNormalCompletion4 && _iterator4.return != null && _iterator4.return();
} finally {
if (_didIteratorError4) throw _iteratorError4;
}
}
}
return buildInfo && (built.loaderPath = buildInfo.loaderPath), built;
}
var 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(toAbsolute("dist/buildInfo.json"), buildInfoForWriting);
var postBuildLogs = [],
platform = (_oneOptions_web = oneOptions.web) === null || _oneOptions_web === void 0 ? void 0 : _oneOptions_web.deploy;
switch (platform && postBuildLogs.push(`[one.build] platform ${platform}`), platform) {
case "vercel":
{
await buildVercelOutputDirectory({
apiOutput,
buildInfoForWriting,
clientDir,
oneOptionsRoot: options.root,
postBuildLogs
});
break;
}
}
process.env.VXRN_ANALYZE_BUNDLE && postBuildLogs.push(`client build report: ${toAbsolute("dist/report.html")}`), postBuildLogs.length && (console.info(`
`), postBuildLogs.forEach(function (log) {
console.info(` \xB7 ${log}`);
})), console.info(`
\u{1F49B} build complete
`);
}
var TRAILING_INDEX_REGEX = /\/index(\.(web))?/;
function getCleanPaths(possiblePaths) {
return Array.from(new Set(Array.from(new Set(possiblePaths)).flatMap(function (p) {
var paths = [p];
if (p.match(TRAILING_INDEX_REGEX)) {
var pathWithTrailingIndexRemoved = p.replace(TRAILING_INDEX_REGEX, "");
paths.push(pathWithTrailingIndexRemoved), paths.push(pathWithTrailingIndexRemoved + "/");
}
return paths;
})));
}
async function moveAllFiles(src, dest) {
try {
await FSExtra.copy(src, dest, {
overwrite: !0,
errorOnExist: !1
});
} catch (err) {
console.error("Error moving files:", err);
}
}
export { build };
//# sourceMappingURL=build.native.js.map