UNPKG

gatsby-adapter-netlify

Version:
310 lines (299 loc) 10.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.prepareFunction = prepareFunction; exports.prepareFunctionVariants = prepareFunctionVariants; var _package = _interopRequireDefault(require("gatsby-adapter-netlify/package.json")); var _fsExtra = _interopRequireDefault(require("fs-extra")); var path = _interopRequireWildcard(require("path")); var _path2 = require("gatsby-core-utils/path"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } async function prepareFunction(fun, odbfunctionName) { var _packageJson$version; let functionId = fun.functionId; let isODB = false; if (odbfunctionName) { functionId = odbfunctionName; isODB = true; } const internalFunctionsDir = path.join(process.cwd(), `.netlify`, `functions-internal`, functionId); await _fsExtra.default.ensureDir(internalFunctionsDir); // This is a temporary hacky approach, eventually it should be just `fun.name` const displayName = isODB ? `DSG` : fun.name === `SSR & DSG` ? `SSR` : fun.name; const functionManifest = { config: { name: displayName, generator: `gatsby-adapter-netlify@${(_packageJson$version = _package.default === null || _package.default === void 0 ? void 0 : _package.default.version) !== null && _packageJson$version !== void 0 ? _packageJson$version : `unknown`}`, includedFiles: fun.requiredFiles.map(file => (0, _path2.slash)(file).replace(/\[/g, `*`).replace(/]/g, `*`)), includedFilesBasePath: process.cwd(), externalNodeModules: [`msgpackr-extract`] }, version: 1 }; await _fsExtra.default.writeJSON(path.join(internalFunctionsDir, `${functionId}.json`), functionManifest); function getRelativePathToModule(modulePath) { const absolutePath = require.resolve(modulePath); return `./` + path.posix.relative((0, _path2.slash)(internalFunctionsDir), (0, _path2.slash)(absolutePath)); } const handlerSource = /* javascript */` const Stream = require("stream") const http = require("http") const { Buffer } = require("buffer") const cookie = require("${getRelativePathToModule(`cookie`)}") ${isODB ? `const { builder } = require("${getRelativePathToModule(`@netlify/functions`)}")` : ``} const preferDefault = m => (m && m.default) || m const functionModule = require("${getRelativePathToModule(path.join(process.cwd(), fun.pathToEntryPoint))}") const functionHandler = preferDefault(functionModule) const statuses = { "100": "Continue", "101": "Switching Protocols", "102": "Processing", "103": "Early Hints", "200": "OK", "201": "Created", "202": "Accepted", "203": "Non-Authoritative Information", "204": "No Content", "205": "Reset Content", "206": "Partial Content", "207": "Multi-Status", "208": "Already Reported", "226": "IM Used", "300": "Multiple Choices", "301": "Moved Permanently", "302": "Found", "303": "See Other", "304": "Not Modified", "305": "Use Proxy", "307": "Temporary Redirect", "308": "Permanent Redirect", "400": "Bad Request", "401": "Unauthorized", "402": "Payment Required", "403": "Forbidden", "404": "Not Found", "405": "Method Not Allowed", "406": "Not Acceptable", "407": "Proxy Authentication Required", "408": "Request Timeout", "409": "Conflict", "410": "Gone", "411": "Length Required", "412": "Precondition Failed", "413": "Payload Too Large", "414": "URI Too Long", "415": "Unsupported Media Type", "416": "Range Not Satisfiable", "417": "Expectation Failed", "418": "I'm a Teapot", "421": "Misdirected Request", "422": "Unprocessable Entity", "423": "Locked", "424": "Failed Dependency", "425": "Too Early", "426": "Upgrade Required", "428": "Precondition Required", "429": "Too Many Requests", "431": "Request Header Fields Too Large", "451": "Unavailable For Legal Reasons", "500": "Internal Server Error", "501": "Not Implemented", "502": "Bad Gateway", "503": "Service Unavailable", "504": "Gateway Timeout", "505": "HTTP Version Not Supported", "506": "Variant Also Negotiates", "507": "Insufficient Storage", "508": "Loop Detected", "509": "Bandwidth Limit Exceeded", "510": "Not Extended", "511": "Network Authentication Required" } const createRequestObject = ({ event, context }) => { const { path = "", multiValueQueryStringParameters, queryStringParameters, httpMethod, multiValueHeaders = {}, body, isBase64Encoded, rawUrl } = event const newStream = new Stream.Readable() const req = Object.assign(newStream, http.IncomingMessage.prototype) req.url = path req.originalUrl = req.url req.rawUrl = rawUrl req.query = queryStringParameters req.multiValueQuery = multiValueQueryStringParameters req.method = httpMethod req.rawHeaders = [] req.headers = {} // Expose Netlify Function event and context on request object. req.netlifyFunctionParams = { event, context } for (const key of Object.keys(multiValueHeaders)) { for (const value of multiValueHeaders[key]) { req.rawHeaders.push(key, value) } req.headers[key.toLowerCase()] = multiValueHeaders[key].toString() } req.getHeader = name => req.headers[name.toLowerCase()] req.getHeaders = () => req.headers // Gatsby includes cookie middleware const cookies = req.headers.cookie if (cookies) { req.cookies = cookie.parse(cookies) } // req.connection = {} if (body) { req.push(body, isBase64Encoded ? "base64" : undefined) } req.push(null) return req } const createResponseObject = ({ onResEnd }) => { const response = { isBase64Encoded: true, multiValueHeaders: {}, }; const res = new Stream(); Object.defineProperty(res, 'statusCode', { get() { return response.statusCode; }, set(statusCode) { response.statusCode = statusCode; }, }); res.headers = { 'content-type': 'text/html; charset=utf-8' }; res.writeHead = (status, headers) => { response.statusCode = status; if (headers) { res.headers = Object.assign(res.headers, headers); } // Return res object to allow for chaining // Fixes: https://github.com/netlify/next-on-netlify/pull/74 return res; }; res.write = (chunk) => { if (!response.body) { response.body = Buffer.from(''); } response.body = Buffer.concat([ Buffer.isBuffer(response.body) ? response.body : Buffer.from(response.body), Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk), ]); return true; }; res.setHeader = (name, value) => { res.headers[name.toLowerCase()] = value; return res; }; res.removeHeader = (name) => { delete res.headers[name.toLowerCase()]; }; res.getHeader = (name) => res.headers[name.toLowerCase()]; res.getHeaders = () => res.headers; res.hasHeader = (name) => Boolean(res.getHeader(name)); res.end = (text) => { if (text) res.write(text); if (!res.statusCode) { res.statusCode = 200; } if (response.body) { response.body = Buffer.from(response.body).toString('base64'); } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore These types are a mess, and need sorting out response.multiValueHeaders = res.headers; res.writeHead(response.statusCode); // Convert all multiValueHeaders into arrays for (const key of Object.keys(response.multiValueHeaders)) { const header = response.multiValueHeaders[key]; if (!Array.isArray(header)) { response.multiValueHeaders[key] = [header]; } } res.finished = true; res.writableEnded = true; // Call onResEnd handler with the response object onResEnd(response); return res; }; // Gatsby Functions additions res.send = (data) => { if (res.finished) { return res; } if (typeof data === 'number') { return res .status(data) .setHeader('content-type', 'text/plain; charset=utf-8') .end(statuses[data] || String(data)); } if (typeof data === 'boolean' || typeof data === 'object') { if (Buffer.isBuffer(data)) { res.setHeader('content-type', 'application/octet-Stream'); } else if (data !== null) { return res.json(data); } } res.end(data); return res; }; res.json = (data) => { if (res.finished) { return res; } res.setHeader('content-type', 'application/json'); res.end(JSON.stringify(data)); return res; }; res.status = (code) => { const numericCode = Number.parseInt(code); if (!Number.isNaN(numericCode)) { response.statusCode = numericCode; } return res; }; res.redirect = (statusCodeOrUrl, url) => { let statusCode = statusCodeOrUrl; let Location = url; if (!url && typeof statusCodeOrUrl === 'string') { Location = statusCodeOrUrl; statusCode = 302; } res.writeHead(statusCode, { Location }); res.end(); return res; }; return res; }; const handler = async (event, context) => { const req = createRequestObject({ event, context }) return new Promise(async resolve => { try { const res = createResponseObject({ onResEnd: resolve }) await functionHandler(req, res) } catch(error) { console.error("Error executing " + event.path, error) resolve({ statusCode: 500 }) } }) } exports.handler = ${isODB ? `builder(handler)` : `handler`} `; await _fsExtra.default.writeFile(path.join(internalFunctionsDir, `${functionId}.js`), handlerSource); } async function prepareFunctionVariants(fun, odbfunctionName) { await prepareFunction(fun); if (odbfunctionName) { await prepareFunction(fun, odbfunctionName); } }