import fs from "fs"; import path from "path"; export default async function run({ renderFunctionFilePath, routePatterns, apiRoutePatterns, portsFilePath, htmlTemplate, }) { console.log("Running adapter script"); ensureDirSync("functions/render"); ensureDirSync("functions/server-render"); fs.copyFileSync( renderFunctionFilePath, "./functions/render/elm-pages-cli.js" ); fs.copyFileSync( renderFunctionFilePath, "./functions/server-render/elm-pages-cli.js" ); fs.copyFileSync(portsFilePath, "./functions/render/port-data-source.js"); fs.copyFileSync( portsFilePath, "./functions/server-render/port-data-source.js" ); fs.writeFileSync( "./functions/render/index.js", rendererCode(true, htmlTemplate) ); fs.writeFileSync( "./functions/server-render/index.js", rendererCode(false, htmlTemplate) ); // TODO rename functions/render to functions/fallback-render // TODO prepend instead of writing file const apiServerRoutes = apiRoutePatterns.filter(isServerSide); ensureValidRoutePatternsForNetlify(apiServerRoutes); // TODO filter apiRoutePatterns on is server side // TODO need information on whether api route is odb or serverless const apiRouteRedirects = apiServerRoutes .map((apiRoute) => { if (apiRoute.kind === "prerender-with-fallback") { return `${apiPatternToRedirectPattern( apiRoute.pathPattern )} /.netlify/builders/render 200`; } else if (apiRoute.kind === "serverless") { return `${apiPatternToRedirectPattern( apiRoute.pathPattern )} /.netlify/functions/server-render 200`; } else { throw "Unhandled 2"; } }) .join("\n"); const redirectsFile = routePatterns .filter(isServerSide) .map((route) => { if (route.kind === "prerender-with-fallback") { return `${route.pathPattern} /.netlify/builders/render 200 ${route.pathPattern}/content.dat /.netlify/builders/render 200`; } else { return `${route.pathPattern} /.netlify/functions/server-render 200 ${path.join( route.pathPattern, "/content.dat" )} /.netlify/functions/server-render 200`; } }) .join("\n") + "\n" + apiRouteRedirects + "\n"; fs.writeFileSync("dist/_redirects", redirectsFile); } function ensureValidRoutePatternsForNetlify(apiRoutePatterns) { const invalidNetlifyRoutes = apiRoutePatterns.filter((apiRoute) => apiRoute.pathPattern.some(({ kind }) => kind === "hybrid") ); if (invalidNetlifyRoutes.length > 0) { throw ( "Invalid Netlify routes!\n" + invalidNetlifyRoutes .map((value) => JSON.stringify(value, null, 2)) .join(", ") ); } } function isServerSide(route) { return ( route.kind === "prerender-with-fallback" || route.kind === "serverless" ); } /** * @param {boolean} isOnDemand * @param {string} htmlTemplate */ function rendererCode(isOnDemand, htmlTemplate) { return `const path = require("path"); const busboy = require("busboy"); const htmlTemplate = ${JSON.stringify(htmlTemplate)}; ${ isOnDemand ? `const { builder } = require("@netlify/functions"); exports.handler = builder(render);` : ` exports.handler = render;` } /** * @param {import('aws-lambda').APIGatewayProxyEvent} event * @param {any} context */ async function render(event, context) { const requestTime = new Date(); console.log(JSON.stringify(event)); global.staticHttpCache = {}; const compiledElmPath = path.join(__dirname, "elm-pages-cli.js"); const compiledPortsFile = path.join(__dirname, "port-data-source.js"); const renderer = require("../../../../generator/src/render"); const preRenderHtml = require("../../../../generator/src/pre-render-html"); try { const basePath = "/"; const mode = "build"; const addWatcher = () => {}; const renderResult = await renderer( compiledPortsFile, basePath, require(compiledElmPath), mode, event.path, await reqToJson(event, requestTime), addWatcher, false ); console.log("@@@renderResult", JSON.stringify(renderResult, null, 2)); const statusCode = renderResult.is404 ? 404 : renderResult.statusCode; if (renderResult.kind === "bytes") { return { body: Buffer.from(renderResult.contentDatPayload.buffer).toString("base64"), isBase64Encoded: true, headers: { "Content-Type": "application/octet-stream", "x-powered-by": "elm-pages", ...renderResult.headers, }, statusCode, }; } else if (renderResult.kind === "api-response") { const serverResponse = renderResult.body; return { body: serverResponse.body, multiValueHeaders: serverResponse.headers, statusCode: serverResponse.statusCode, isBase64Encoded: serverResponse.isBase64Encoded, }; } else { console.log('@rendering', preRenderHtml.replaceTemplate(htmlTemplate, renderResult.htmlString)) return { body: preRenderHtml.replaceTemplate(htmlTemplate, renderResult.htmlString), headers: { "Content-Type": "text/html", "x-powered-by": "elm-pages", ...renderResult.headers, }, statusCode, }; } } catch (error) { console.error(error); console.error(JSON.stringify(error, null, 2)); return { body: \`
\${JSON.stringify(error, null, 2)}\`, statusCode: 500, headers: { "Content-Type": "text/html", "x-powered-by": "elm-pages", }, }; } } /** * @param {import('aws-lambda').APIGatewayProxyEvent} req * @param {Date} requestTime * @returns {Promise<{ method: string; hostname: string; query: Record