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

Commit e7c8641

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 80062d3 commit e7c8641

File tree

7 files changed

+197
-13
lines changed

7 files changed

+197
-13
lines changed

lib/config.js

Lines changed: 5 additions & 5 deletions
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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const { copySync } = require("fs-extra");
22
const { join } = require("path");
33
const {
44
NEXT_DIST_DIR,
5+
TEMPLATES_DIR,
56
NETLIFY_FUNCTIONS_PATH,
67
FUNCTION_TEMPLATE_PATH,
78
} = require("../config");
@@ -23,6 +24,14 @@ const setupNetlifyFunctionForPage = (filePath) => {
2324
errorOnExist: true,
2425
});
2526

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

lib/templates/compat.js

Lines changed: 22 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 159 additions & 0 deletions
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

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
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)