UNPKG

one

Version:

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

92 lines (91 loc) 5.31 kB
import { dirname, join, resolve } from "node:path"; import generator from "@babel/generator"; import parser from "@babel/parser"; import traverse from "@babel/traverse"; import t from "@babel/types"; import { resolvePath } from "@vxrn/resolve"; import FSExtra from "fs-extra"; import fs from "fs-extra"; import { serverlessVercelNodeJsConfig } from "../config/vc-config-base.mjs"; import { serverlessVercelPackageJson } from "../config/vc-package-base.mjs"; import { getPathFromRoute } from "../getPathFromRoute.mjs"; async function createApiServerlessFunction(route, code, oneOptionsRoot, postBuildLogs, outDir = "dist") { try { const path = getPathFromRoute(route, { includeIndex: true }); postBuildLogs.push(`[one.build][vercel.createSsrServerlessFunction] pageName: ${path}`); const funcFolder = join(oneOptionsRoot, `.vercel/output/functions/${path}.func`); await fs.ensureDir(funcFolder); if (code.includes("react")) { postBuildLogs.push(`[one.build][vercel.createSsrServerlessFunction] detected react in depenency tree for ${path}`); const reactPath = dirname(resolvePath("react/package.json", oneOptionsRoot)); await fs.copy(resolve(reactPath), resolve(join(funcFolder, "node_modules", "react"))); } const distAssetsFolder = resolve(join(funcFolder, "assets")); postBuildLogs.push(`[one.build][vercel.createSsrServerlessFunction] copy shared assets to ${distAssetsFolder}`); const sourceAssetsFolder = resolve(join(oneOptionsRoot, outDir, "api", "assets")); if (await FSExtra.pathExists(sourceAssetsFolder)) { await fs.copy(sourceAssetsFolder, distAssetsFolder); } await fs.ensureDir(resolve(join(funcFolder, "entrypoint"))); const entrypointFilePath = resolve(join(funcFolder, "entrypoint", "index.js")); postBuildLogs.push(`[one.build][vercel.createSsrServerlessFunction] writing entrypoint to ${entrypointFilePath}`); await fs.writeFile(entrypointFilePath, wrapHandlerFunctions(code)); const packageJsonFilePath = resolve(join(funcFolder, "package.json")); postBuildLogs.push(`[one.build][vercel.createSsrServerlessFunction] writing package.json to ${packageJsonFilePath}`); await fs.writeJSON(packageJsonFilePath, serverlessVercelPackageJson); postBuildLogs.push(`[one.build][vercel.createSsrServerlessFunction] writing .vc-config.json to ${join(funcFolder, ".vc-config.json")}`); return fs.writeJson(join(funcFolder, ".vc-config.json"), { ...serverlessVercelNodeJsConfig, handler: "entrypoint/index.js" }); } catch (e) { console.error(`[one.build][vercel.createSsrServerlessFunction] failed to generate func for ${route.file}`, e); } } function wrapHandlerFunctions(code) { const ast = parser.parse(code, { sourceType: "module" }); traverse.default(ast, { FunctionDeclaration(path) { const { node } = path; const functionNamesToHandle = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS" // TODO: more possibilities? ]; if (!node.id || !functionNamesToHandle.includes(node.id.name)) return; if (node.extra && node.extra.isWrapper) return; if (node.extra && node.extra.isWrapped) return; const originalName = `orig_${node.id.name}`; const originalFunction = t.functionDeclaration(t.identifier(originalName), node.params, node.body, node.generator, node.async); const requestIdentifier = t.identifier("request"); const wrapperParams = [requestIdentifier]; const urlIdentifier = t.identifier("url"); const paramsIdentifier = t.identifier("params"); const urlDecl = t.variableDeclaration("const", [t.variableDeclarator(urlIdentifier, t.newExpression(t.identifier("URL"), [t.memberExpression(requestIdentifier, t.identifier("url"))]))]); const paramsDecl = t.variableDeclaration("const", [t.variableDeclarator(paramsIdentifier, t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("fromEntries")), [t.callExpression(t.memberExpression(t.memberExpression(urlIdentifier, t.identifier("searchParams")), t.identifier("entries")), [])]))]); const callOrigFnStatement = t.callExpression(t.identifier(originalName), [requestIdentifier, t.objectExpression([t.objectProperty(t.identifier("params"), paramsIdentifier)])]); const wrapperFunction = t.functionDeclaration(t.identifier(node.id.name + ""), wrapperParams, t.blockStatement([urlDecl, paramsDecl, t.returnStatement(callOrigFnStatement)]) // No need to care if the wrapper function should be async, // since we didn't use any await in the wrapper function, and we'll // just return what the original function returns. ); node.extra = node.extra || {}; node.extra.isWrapped = true; wrapperFunction.extra = wrapperFunction.extra || {}; wrapperFunction.extra.isWrapper = true; if (path.parentPath.isExportNamedDeclaration()) { path.replaceWithMultiple([originalFunction, t.exportNamedDeclaration(wrapperFunction, [])]); } else { path.replaceWithMultiple([originalFunction, wrapperFunction]); } } }); const output = generator.default(ast, {}).code; return output; } export { createApiServerlessFunction }; //# sourceMappingURL=createApiServerlessFunction.mjs.map