1
1
import { IncomingMessage , ServerResponse } from 'http' ;
2
+ import { UrlWithParsedQuery } from 'url' ;
3
+
4
+ import {
5
+ imageOptimizer as pixel ,
6
+ ImageOptimizerOptions ,
7
+ } from '@millihq/pixel-core' ;
2
8
import { ImageConfig } from 'next/dist/server/image-config' ;
3
- import { NextConfig } from 'next/dist/server/config' ;
4
- import { imageOptimizer as nextImageOptimizer } from 'next/dist/server/image-optimizer' ;
5
- import Server from 'next/dist/server/next-server' ;
6
9
import nodeFetch from 'node-fetch' ;
7
- import { UrlWithParsedQuery } from 'url' ;
8
10
import S3 from 'aws-sdk/clients/s3' ;
9
11
10
12
/* -----------------------------------------------------------------------------
11
13
* Types
12
14
* ---------------------------------------------------------------------------*/
13
15
14
- type NodeFetch = typeof nodeFetch ;
15
-
16
- type OriginCacheControl = string | null ;
17
-
18
16
interface S3Config {
19
17
s3 : S3 ;
20
18
bucket : string ;
21
19
}
22
20
23
- type ImageOptimizerResult = {
24
- finished : boolean ;
25
- originCacheControl : OriginCacheControl ;
26
- } ;
27
-
28
- /* -----------------------------------------------------------------------------
29
- * globals
30
- * ---------------------------------------------------------------------------*/
31
-
32
- // Sets working dir of Next.js to /tmp (Lambda tmp dir)
33
- const distDir = '/tmp' ;
34
-
35
- let originCacheControl : OriginCacheControl ;
36
-
37
- /**
38
- * fetch polyfill to intercept the request to the external resource
39
- * to get the Cache-Control header from the origin
40
- */
41
- const fetchPolyfill : NodeFetch = ( url , init ) => {
42
- return nodeFetch ( url , init ) . then ( ( result ) => {
43
- originCacheControl = result . headers . get ( 'Cache-Control' ) ;
44
- return result ;
45
- } ) ;
46
- } ;
47
-
48
- fetchPolyfill . isRedirect = nodeFetch . isRedirect ;
49
-
50
- // Polyfill fetch is used by nextImageOptimizer
51
- // @ts -ignore
52
- global . fetch = fetchPolyfill ;
53
-
54
21
/* -----------------------------------------------------------------------------
55
22
* imageOptimizer
56
23
* ---------------------------------------------------------------------------*/
@@ -61,19 +28,31 @@ async function imageOptimizer(
61
28
res : ServerResponse ,
62
29
parsedUrl : UrlWithParsedQuery ,
63
30
s3Config ?: S3Config
64
- ) : Promise < ImageOptimizerResult > {
65
- // Create next config mock
66
- const nextConfig = ( {
67
- images : imageConfig ,
68
- } as unknown ) as NextConfig ;
69
-
70
- // Create Next Server mock
71
- const server = {
72
- getRequestHandler : ( ) => async (
31
+ ) : ReturnType < typeof pixel > {
32
+ const options : ImageOptimizerOptions = {
33
+ /**
34
+ * Use default temporary folder from AWS Lambda
35
+ */
36
+ distDir : '/tmp' ,
37
+
38
+ imageConfig : {
39
+ ...imageConfig ,
40
+ loader : 'default' ,
41
+ } ,
42
+
43
+ /**
44
+ * Is called when the path is an absolute URI, e.g. `/my/image.png`.
45
+ *
46
+ * @param req - Incoming client request
47
+ * @param res - Outgoing mocked response
48
+ * @param url - Parsed url object from the client request,
49
+ * e.g. `/my/image.png`
50
+ */
51
+ async requestHandler (
73
52
{ headers } : IncomingMessage ,
74
53
res : ServerResponse ,
75
54
url : UrlWithParsedQuery
76
- ) => {
55
+ ) {
77
56
if ( s3Config ) {
78
57
// S3 expects keys without leading `/`
79
58
const trimmedKey = url . href . startsWith ( '/' )
@@ -98,10 +77,12 @@ async function imageOptimizer(
98
77
}
99
78
100
79
if ( object . CacheControl ) {
101
- originCacheControl = object . CacheControl ;
80
+ res . setHeader ( 'Cache-Control' , object . CacheControl ) ;
81
+ // originCacheControl = object.CacheControl;
102
82
}
103
83
104
- res . end ( object . Body ) ;
84
+ res . write ( object . Body ) ;
85
+ res . end ( ) ;
105
86
} else if ( headers . referer ) {
106
87
const { referer } = headers ;
107
88
const trimmedReferer = referer . endsWith ( '/' )
@@ -116,30 +97,24 @@ async function imageOptimizer(
116
97
117
98
res . statusCode = upstreamRes . status ;
118
99
const upstreamType = upstreamRes . headers . get ( 'Content-Type' ) ;
119
- originCacheControl = upstreamRes . headers . get ( 'Cache-Control' ) ;
100
+ const originCacheControl = upstreamRes . headers . get ( 'Cache-Control' ) ;
120
101
121
102
if ( upstreamType ) {
122
103
res . setHeader ( 'Content-Type' , upstreamType ) ;
123
104
}
124
105
106
+ if ( originCacheControl ) {
107
+ res . setHeader ( 'Cache-Control' , originCacheControl ) ;
108
+ }
109
+
125
110
const upstreamBuffer = Buffer . from ( await upstreamRes . arrayBuffer ( ) ) ;
126
- res . end ( upstreamBuffer ) ;
111
+ res . write ( upstreamBuffer ) ;
112
+ res . end ( ) ;
127
113
}
128
114
} ,
129
- } as Server ;
130
-
131
- const result = await nextImageOptimizer (
132
- server ,
133
- req ,
134
- res ,
135
- parsedUrl ,
136
- nextConfig ,
137
- distDir
138
- ) ;
139
- return {
140
- ...result ,
141
- originCacheControl,
142
115
} ;
116
+
117
+ return pixel ( req , res , parsedUrl , options ) ;
143
118
}
144
119
145
120
export type { S3Config } ;
0 commit comments