Skip to content
This repository was archived by the owner on May 10, 2021. It is now read-only.

Commit 97b59fd

Browse files
committed
Copy files from next-aws-lambda
Copy over the files from the next-aws-lambda package and manually bundle them into our Netlify Functions. This gives us more flexibility to customize the compatibility layer between Netlify Functions and Next.js. For now, no changes have been made to the next-aws-lambda files and they have been copied as-is. next-aws-lambda source: https://github.com/serverless-nextjs/serverless-next.js/tree/master/packages/compat-layers/apigw-lambda-compat
1 parent 96b7e40 commit 97b59fd

File tree

7 files changed

+201
-14
lines changed

7 files changed

+201
-14
lines changed

lib/config.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ const NEXT_CONFIG_PATH = join(".", "next.config.js");
1919
// This is the folder that NextJS builds to; default is .next
2020
const NEXT_DIST_DIR = getNextDistDir({ nextConfigPath: NEXT_CONFIG_PATH });
2121

22+
// This is the folder with templates for Netlify Functions
23+
const TEMPLATES_DIR = join(__dirname, "templates");
24+
2225
// This is the Netlify Function template that wraps all SSR pages
23-
const FUNCTION_TEMPLATE_PATH = join(
24-
__dirname,
25-
"templates",
26-
"netlifyFunction.js"
27-
);
26+
const FUNCTION_TEMPLATE_PATH = join(TEMPLATES_DIR, "netlifyFunction.js");
2827

2928
// This is the file where custom redirects can be configured
3029
const CUSTOM_REDIRECTS_PATH = join(".", "_redirects");
@@ -35,6 +34,7 @@ module.exports = {
3534
PUBLIC_PATH,
3635
NEXT_CONFIG_PATH,
3736
NEXT_DIST_DIR,
37+
TEMPLATES_DIR,
3838
FUNCTION_TEMPLATE_PATH,
3939
CUSTOM_REDIRECTS_PATH,
4040
};

lib/helpers/setupNetlifyFunctionForPage.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
const { copySync } = require("fs-extra");
22
const { join } = require("path");
3-
const { NEXT_DIST_DIR, FUNCTION_TEMPLATE_PATH } = require("../config");
3+
const {
4+
NEXT_DIST_DIR,
5+
TEMPLATES_DIR,
6+
FUNCTION_TEMPLATE_PATH,
7+
} = require("../config");
48
const getNetlifyFunctionName = require("./getNetlifyFunctionName");
59

610
// Create a Netlify Function for the page with the given file path
@@ -19,6 +23,14 @@ const setupNetlifyFunctionForPage = ({ filePath, functionsPath }) => {
1923
errorOnExist: true,
2024
});
2125

26+
// Copy function helpers
27+
["compat.js", "reqResMapper.js"].forEach((helper) => {
28+
copySync(join(TEMPLATES_DIR, helper), join(functionDirectory, helper), {
29+
overwrite: false,
30+
errorOnExist: true,
31+
});
32+
});
33+
2234
// Copy page
2335
const nextPageCopyPath = join(functionDirectory, "nextJsPage.js");
2436
copySync(join(NEXT_DIST_DIR, "serverless", filePath), nextPageCopyPath, {

lib/templates/compat.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// API Gateway Lambda Compat
2+
// License: MIT
3+
// Source: https://github.com/serverless-nextjs/serverless-next.js/blob/master/packages/compat-layers/apigw-lambda-compat/index.js
4+
5+
const reqResMapper = require("./reqResMapper");
6+
7+
const handlerFactory = (page) => (event, _context, callback) => {
8+
const { req, res, responsePromise } = reqResMapper(event, callback);
9+
if (page.render instanceof Function) {
10+
// Is a React component
11+
page.render(req, res);
12+
} else {
13+
// Is an API
14+
page.default(req, res);
15+
}
16+
17+
if (responsePromise) {
18+
return responsePromise;
19+
}
20+
};
21+
22+
module.exports = handlerFactory;

lib/templates/netlifyFunction.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// running next-on-netlify
33

44
// Compatibility wrapper for NextJS page
5-
const compat = require("next-aws-lambda");
5+
const compat = require("./compat");
66
// Load the NextJS page
77
const page = require("./nextJsPage");
88

lib/templates/reqResMapper.js

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// API Gateway Lambda Compat
2+
// License: MIT
3+
// Source: https://github.com/serverless-nextjs/serverless-next.js/blob/master/packages/compat-layers/apigw-lambda-compat/lib/compatLayer.js
4+
5+
const Stream = require("stream");
6+
const queryString = require("querystring");
7+
const http = require("http");
8+
9+
const reqResMapper = (event, callback) => {
10+
const base64Support = process.env.BINARY_SUPPORT === "yes";
11+
const response = {
12+
isBase64Encoded: base64Support,
13+
multiValueHeaders: {},
14+
};
15+
let responsePromise;
16+
17+
const newStream = new Stream.Readable();
18+
const req = Object.assign(newStream, http.IncomingMessage.prototype);
19+
req.url =
20+
(event.requestContext.path || event.path || "").replace(
21+
new RegExp("^/" + event.requestContext.stage),
22+
""
23+
) || "/";
24+
25+
let qs = "";
26+
27+
if (event.multiValueQueryStringParameters) {
28+
qs += queryString.stringify(event.multiValueQueryStringParameters);
29+
}
30+
31+
if (event.pathParameters) {
32+
const pathParametersQs = queryString.stringify(event.pathParameters);
33+
34+
if (qs.length > 0) {
35+
qs += `&${pathParametersQs}`;
36+
} else {
37+
qs += pathParametersQs;
38+
}
39+
}
40+
41+
const hasQueryString = qs.length > 0;
42+
43+
if (hasQueryString) {
44+
req.url += `?${qs}`;
45+
}
46+
47+
req.method = event.httpMethod;
48+
req.rawHeaders = [];
49+
req.headers = {};
50+
51+
const headers = event.multiValueHeaders || {};
52+
53+
for (const key of Object.keys(headers)) {
54+
for (const value of headers[key]) {
55+
req.rawHeaders.push(key);
56+
req.rawHeaders.push(value);
57+
}
58+
req.headers[key.toLowerCase()] = headers[key].toString();
59+
}
60+
61+
req.getHeader = (name) => {
62+
return req.headers[name.toLowerCase()];
63+
};
64+
req.getHeaders = () => {
65+
return req.headers;
66+
};
67+
68+
req.connection = {};
69+
70+
const res = new Stream();
71+
Object.defineProperty(res, "statusCode", {
72+
get() {
73+
return response.statusCode;
74+
},
75+
set(statusCode) {
76+
response.statusCode = statusCode;
77+
},
78+
});
79+
res.headers = {};
80+
res.writeHead = (status, headers) => {
81+
response.statusCode = status;
82+
if (headers) res.headers = Object.assign(res.headers, headers);
83+
};
84+
res.write = (chunk) => {
85+
if (!response.body) {
86+
response.body = Buffer.from("");
87+
}
88+
89+
response.body = Buffer.concat([
90+
Buffer.isBuffer(response.body)
91+
? response.body
92+
: Buffer.from(response.body),
93+
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk),
94+
]);
95+
};
96+
res.setHeader = (name, value) => {
97+
res.headers[name.toLowerCase()] = value;
98+
};
99+
res.removeHeader = (name) => {
100+
delete res.headers[name.toLowerCase()];
101+
};
102+
res.getHeader = (name) => {
103+
return res.headers[name.toLowerCase()];
104+
};
105+
res.getHeaders = () => {
106+
return res.headers;
107+
};
108+
res.hasHeader = (name) => {
109+
return !!res.getHeader(name);
110+
};
111+
112+
const onResEnd = (callback, resolve) => (text) => {
113+
if (text) res.write(text);
114+
if (!res.statusCode) {
115+
res.statusCode = 200;
116+
}
117+
118+
if (response.body) {
119+
response.body = Buffer.from(response.body).toString(
120+
base64Support ? "base64" : undefined
121+
);
122+
}
123+
response.multiValueHeaders = res.headers;
124+
res.writeHead(response.statusCode);
125+
fixApiGatewayMultipleHeaders();
126+
127+
if (callback) {
128+
callback(null, response);
129+
} else {
130+
resolve(response);
131+
}
132+
};
133+
134+
if (typeof callback === "function") {
135+
res.end = onResEnd(callback);
136+
} else {
137+
responsePromise = new Promise((resolve) => {
138+
res.end = onResEnd(null, resolve);
139+
});
140+
}
141+
142+
if (event.body) {
143+
req.push(event.body, event.isBase64Encoded ? "base64" : undefined);
144+
}
145+
146+
req.push(null);
147+
148+
function fixApiGatewayMultipleHeaders() {
149+
for (const key of Object.keys(response.multiValueHeaders)) {
150+
if (!Array.isArray(response.multiValueHeaders[key])) {
151+
response.multiValueHeaders[key] = [response.multiValueHeaders[key]];
152+
}
153+
}
154+
}
155+
156+
return { req, res, responsePromise };
157+
};
158+
159+
module.exports = reqResMapper;

package-lock.json

-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@
5252
"dependencies": {
5353
"@sls-next/lambda-at-edge": "^1.5.2",
5454
"commander": "^6.0.0",
55-
"fs-extra": "^9.0.1",
56-
"next-aws-lambda": "^2.5.0"
55+
"fs-extra": "^9.0.1"
5756
},
5857
"husky": {
5958
"hooks": {

0 commit comments

Comments
 (0)