@@ -7,7 +7,7 @@ const path = require("path");
7
7
const getPort = require ( "get-port" ) ;
8
8
const chokidar = require ( "chokidar" ) ;
9
9
const jwtDecode = require ( "jwt-decode" ) ;
10
- // const chalk = require("chalk");
10
+ const chalk = require ( "chalk" ) ;
11
11
const {
12
12
NETLIFYDEVLOG ,
13
13
// NETLIFYDEVWARN,
@@ -27,60 +27,10 @@ function handleErr(err, response) {
27
27
console . log ( `${ NETLIFYDEVERR } Error during invocation: ` , err ) ; // eslint-disable-line no-console
28
28
}
29
29
30
- function createCallback ( response ) {
31
- return function ( err , lambdaResponse ) {
32
- if ( err ) {
33
- return handleErr ( err , response ) ;
34
- }
35
- if ( ! Number ( lambdaResponse . statusCode ) ) {
36
- console . log (
37
- `${ NETLIFYDEVERR } Your function response must have a numerical statusCode. You gave: $` ,
38
- lambdaResponse . statusCode
39
- ) ;
40
- return handleErr ( "Incorrect function response statusCode" , response ) ;
41
- }
42
- if ( typeof lambdaResponse . body !== "string" ) {
43
- console . log (
44
- `${ NETLIFYDEVERR } Your function response must have a string body. You gave:` ,
45
- lambdaResponse . body
46
- ) ;
47
- return handleErr ( "Incorrect function response body" , response ) ;
48
- }
49
-
50
- response . statusCode = lambdaResponse . statusCode ;
51
- // eslint-disable-line guard-for-in
52
- for ( const key in lambdaResponse . headers ) {
53
- response . setHeader ( key , lambdaResponse . headers [ key ] ) ;
54
- }
55
- response . write (
56
- lambdaResponse . isBase64Encoded
57
- ? Buffer . from ( lambdaResponse . body , "base64" )
58
- : lambdaResponse . body
59
- ) ;
60
- response . end ( ) ;
61
- } ;
62
- }
63
-
64
- function promiseCallback ( promise , callback ) {
65
- if ( ! promise ) return ;
66
- if ( typeof promise . then !== "function" ) return ;
67
- if ( typeof callback !== "function" ) return ;
68
-
69
- promise . then (
70
- function ( data ) {
71
- callback ( null , data ) ;
72
- } ,
73
- function ( err ) {
74
- callback ( err , null ) ;
75
- }
76
- ) ;
77
- }
78
-
79
30
// function getHandlerPath(functionPath) {
80
31
// if (functionPath.match(/\.js$/)) {
81
32
// return functionPath;
82
33
// }
83
-
84
34
// return path.join(functionPath, `${path.basename(functionPath)}.js`);
85
35
// }
86
36
@@ -130,7 +80,12 @@ function createHandler(dir) {
130
80
131
81
Object . keys ( functions ) . forEach ( name => {
132
82
const fn = functions [ name ] ;
133
- const clearCache = ( ) => {
83
+ const clearCache = action => ( ) => {
84
+ console . log (
85
+ `${ NETLIFYDEVLOG } function ${ chalk . yellow (
86
+ name
87
+ ) } ${ action } , reloading...`
88
+ ) ; // eslint-disable-line no-console
134
89
const before = module . paths ;
135
90
module . paths = [ fn . moduleDir ] ;
136
91
delete require . cache [ require . resolve ( fn . functionPath ) ] ;
@@ -144,9 +99,9 @@ function createHandler(dir) {
144
99
ignored : / n o d e _ m o d u l e s /
145
100
} ) ;
146
101
fn . watcher
147
- . on ( "add" , clearCache )
148
- . on ( "change" , clearCache )
149
- . on ( "unlink" , clearCache ) ;
102
+ . on ( "add" , clearCache ( "added" ) )
103
+ . on ( "change" , clearCache ( "modified" ) )
104
+ . on ( "unlink" , clearCache ( "deleted" ) ) ;
150
105
} ) ;
151
106
152
107
return function ( request , response ) {
@@ -167,6 +122,11 @@ function createHandler(dir) {
167
122
try {
168
123
module . paths = [ moduleDir ] ;
169
124
handler = require ( functionPath ) ;
125
+ if ( typeof handler . handler !== "function" ) {
126
+ throw new Error (
127
+ `function ${ functionPath } must export a function named handler`
128
+ ) ;
129
+ }
170
130
module . paths = before ;
171
131
} catch ( error ) {
172
132
module . paths = before ;
@@ -180,7 +140,7 @@ function createHandler(dir) {
180
140
if ( body instanceof Buffer ) {
181
141
isBase64Encoded = true ;
182
142
body = body . toString ( "base64" ) ;
183
- } else if ( typeof ( body ) === "string" ) {
143
+ } else if ( typeof body === "string" ) {
184
144
// body is already processed as string
185
145
} else {
186
146
body = "" ;
@@ -195,24 +155,96 @@ function createHandler(dir) {
195
155
isBase64Encoded : isBase64Encoded
196
156
} ;
197
157
158
+ let callbackWasCalled = false ;
198
159
const callback = createCallback ( response ) ;
199
160
const promise = handler . handler (
200
161
lambdaRequest ,
201
162
{ clientContext : buildClientContext ( request . headers ) || { } } ,
202
163
callback
203
164
) ;
204
- promiseCallback ( promise , callback ) ;
165
+ /** guard against using BOTH async and callback */
166
+ if ( callbackWasCalled && promise && typeof promise . then === "function" ) {
167
+ throw new Error (
168
+ "Error: your function seems to be using both a callback and returning a promise (aka async function). This is invalid, pick one. (Hint: async!)"
169
+ ) ;
170
+ } else {
171
+ // it is definitely an async function with no callback called, good.
172
+ promiseCallback ( promise , callback ) ;
173
+ }
174
+
175
+ /** need to keep createCallback in scope so we can know if cb was called AND handler is async */
176
+ function createCallback ( response ) {
177
+ return function ( err , lambdaResponse ) {
178
+ callbackWasCalled = true ;
179
+ if ( err ) {
180
+ return handleErr ( err , response ) ;
181
+ }
182
+ if ( lambdaResponse === undefined ) {
183
+ return handleErr (
184
+ "lambda response was undefined. check your function code again." ,
185
+ response
186
+ ) ;
187
+ }
188
+ if ( ! Number ( lambdaResponse . statusCode ) ) {
189
+ console . log (
190
+ `${ NETLIFYDEVERR } Your function response must have a numerical statusCode. You gave: $` ,
191
+ lambdaResponse . statusCode
192
+ ) ;
193
+ return handleErr ( "Incorrect function response statusCode" , response ) ;
194
+ }
195
+ if ( typeof lambdaResponse . body !== "string" ) {
196
+ console . log (
197
+ `${ NETLIFYDEVERR } Your function response must have a string body. You gave:` ,
198
+ lambdaResponse . body
199
+ ) ;
200
+ return handleErr ( "Incorrect function response body" , response ) ;
201
+ }
202
+
203
+ response . statusCode = lambdaResponse . statusCode ;
204
+ // eslint-disable-line guard-for-in
205
+ for ( const key in lambdaResponse . headers ) {
206
+ response . setHeader ( key , lambdaResponse . headers [ key ] ) ;
207
+ }
208
+ response . write (
209
+ lambdaResponse . isBase64Encoded
210
+ ? Buffer . from ( lambdaResponse . body , "base64" )
211
+ : lambdaResponse . body
212
+ ) ;
213
+ response . end ( ) ;
214
+ } ;
215
+ }
205
216
} ;
206
217
}
207
218
219
+ function promiseCallback ( promise , callback ) {
220
+ if ( ! promise ) return ; // means no handler was written
221
+ if ( typeof promise . then !== "function" ) return ;
222
+ if ( typeof callback !== "function" ) return ;
223
+
224
+ promise . then (
225
+ function ( data ) {
226
+ console . log ( "hellooo" ) ;
227
+ callback ( null , data ) ;
228
+ } ,
229
+ function ( err ) {
230
+ callback ( err , null ) ;
231
+ }
232
+ ) ;
233
+ }
234
+
208
235
async function serveFunctions ( settings ) {
209
236
const app = express ( ) ;
210
237
const dir = settings . functionsDir ;
211
238
const port = await getPort ( {
212
239
port : assignLoudly ( settings . port , defaultPort )
213
240
} ) ;
214
241
215
- app . use ( bodyParser . text ( { limit : "6mb" , type : [ "text/*" , "application/json" , "multipart/form-data" ] } ) ) ;
242
+ app . use (
243
+ bodyParser . text ( {
244
+ limit : "6mb" ,
245
+ type : [ "text/*" , "application/json" , "multipart/form-data" ]
246
+ } )
247
+ ) ;
216
248
app . use ( bodyParser . raw ( { limit : "6mb" , type : "*/*" } ) ) ;
217
249
app . use (
218
250
expressLogging ( console , {
0 commit comments