1
- import { readFile } from 'fs'
2
1
import { createServer as createHttpsServer } from 'https'
3
2
import { createServer } from 'http'
4
3
import { resolve } from 'path'
5
4
6
- import mime from 'mime'
7
5
import opener from 'opener'
6
+ import sirv from 'sirv'
7
+ import compose from 'connect-compose'
8
8
9
9
let server
10
10
11
11
/**
12
12
* Serve your rolled up bundle like webpack-dev-server
13
13
* @param {ServeOptions|string|string[] } options
14
14
*/
15
- function serve ( options = { contentBase : '' } ) {
15
+ export default function serve ( options = { contentBase : '' } ) {
16
16
if ( Array . isArray ( options ) || typeof options === 'string' ) {
17
17
options = { contentBase : options }
18
18
}
19
- options . contentBase = Array . isArray ( options . contentBase ) ? options . contentBase : [ options . contentBase || '' ]
19
+ options . contentBase = Array . isArray ( options . contentBase )
20
+ ? options . contentBase
21
+ : [ options . contentBase || '' ]
20
22
options . port = options . port || 10001
21
23
options . headers = options . headers || { }
22
24
options . https = options . https || false
23
25
options . openPage = options . openPage || ''
24
- mime . default_type = 'text/plain'
25
26
26
- const requestListener = ( request , response ) => {
27
- // Remove querystring
28
- const urlPath = decodeURI ( request . url . split ( '?' ) [ 0 ] )
29
-
30
- Object . keys ( options . headers ) . forEach ( ( key ) => {
31
- response . setHeader ( key , options . headers [ key ] )
32
- } )
33
-
34
- readFileFromContentBase ( options . contentBase , urlPath , function ( error , content , filePath ) {
35
- if ( ! error ) {
36
- return found ( response , filePath , content )
37
- }
38
- if ( error . code !== 'ENOENT' ) {
39
- response . writeHead ( 500 )
40
- response . end ( '500 Internal Server Error' +
41
- '\n\n' + filePath +
42
- '\n\n' + Object . values ( error ) . join ( '\n' ) +
43
- '\n\n(rollup-plugin-serve)' , 'utf-8' )
44
- return
45
- }
46
- if ( options . historyApiFallback ) {
47
- const fallbackPath = typeof options . historyApiFallback === 'string' ? options . historyApiFallback : '/index.html'
48
- readFileFromContentBase ( options . contentBase , fallbackPath , function ( error , content , filePath ) {
49
- if ( error ) {
50
- notFound ( response , filePath )
51
- } else {
52
- found ( response , filePath , content )
53
- }
54
- } )
55
- } else {
56
- notFound ( response , filePath )
57
- }
58
- } )
59
- }
60
-
61
- // release previous server instance if rollup is reloading configuration in watch mode
27
+ // Release previous server instance if rollup is reloading configuration in watch mode
62
28
if ( server ) {
63
29
server . close ( )
64
30
} else {
65
31
closeServerOnTermination ( )
66
32
}
67
33
34
+ // Serve all folders
35
+ const middlewares = options . contentBase . map ( base => sirv ( base , { dev : true } ) )
36
+
37
+ // Send custom headers
38
+ if ( options . headers ) {
39
+ middlewares . unshift ( setHeaders )
40
+ }
41
+
42
+ // Fallback to another page
43
+ let { historyApiFallback : fallback } = options
44
+ if ( fallback ) {
45
+ // Defaults to index.html, sirv know where to look
46
+ fallback = typeof fallback === 'string' ? fallback : '/'
47
+
48
+ // Must start with /
49
+ fallback = ( fallback . startsWith ( '/' ) ? '' : '/' ) + fallback
50
+
51
+ // Swap out the requested page with the fallback page
52
+ middlewares . push ( ( req , res , next ) => {
53
+ req . originalUrl = req . url
54
+ req . url = fallback
55
+ next ( )
56
+ } )
57
+
58
+ // Serve the static files again, this time looking for the fallback page
59
+ const serveStatic = middlewares . slice ( - 3 , - 1 )
60
+ serveStatic . forEach ( middleware => middlewares . push ( middleware ) )
61
+ }
62
+
63
+ middlewares . push ( errorPage )
64
+
65
+ // Combine all middlewares into one
66
+ const app = compose ( middlewares )
67
+
68
68
// If HTTPS options are available, create an HTTPS server
69
69
if ( options . https ) {
70
- server = createHttpsServer ( options . https , requestListener ) . listen ( options . port , options . host )
70
+ server = createHttpsServer ( options . https , app ) . listen (
71
+ options . port ,
72
+ options . host
73
+ )
71
74
} else {
72
- server = createServer ( requestListener ) . listen ( options . port , options . host )
75
+ server = createServer ( app ) . listen ( options . port , options . host )
73
76
}
74
77
75
78
let running = options . verbose === false
76
79
77
80
return {
78
81
name : 'serve' ,
79
- generateBundle ( ) {
82
+ generateBundle ( ) {
80
83
if ( ! running ) {
81
84
running = true
82
85
83
86
// Log which url to visit
84
- const url = ( options . https ? 'https' : 'http' ) + '://' + ( options . host || 'localhost' ) + ':' + options . port
87
+ const url =
88
+ ( options . https ? 'https' : 'http' ) +
89
+ '://' +
90
+ ( options . host || 'localhost' ) +
91
+ ':' +
92
+ options . port
85
93
options . contentBase . forEach ( base => {
86
94
console . log ( green ( url ) + ' -> ' + resolve ( base ) )
87
95
} )
@@ -97,40 +105,24 @@ function serve (options = { contentBase: '' }) {
97
105
}
98
106
}
99
107
}
100
- }
101
108
102
- function readFileFromContentBase ( contentBase , urlPath , callback ) {
103
- let filePath = resolve ( contentBase [ 0 ] || '.' , '.' + urlPath )
104
-
105
- // Load index.html in directories
106
- if ( urlPath . endsWith ( '/' ) ) {
107
- filePath = resolve ( filePath , 'index.html' )
109
+ function setHeaders ( req , res , next ) {
110
+ Object . keys ( options . headers ) . forEach ( key => {
111
+ res . setHeader ( key , options . headers [ key ] )
112
+ } )
113
+ next ( )
108
114
}
109
115
110
- readFile ( filePath , ( error , content ) => {
111
- if ( error && contentBase . length > 1 ) {
112
- // Try to read from next contentBase
113
- readFileFromContentBase ( contentBase . slice ( 1 ) , urlPath , callback )
114
- } else {
115
- // We know enough
116
- callback ( error , content , filePath )
117
- }
118
- } )
119
- }
120
-
121
- function notFound ( response , filePath ) {
122
- response . writeHead ( 404 )
123
- response . end ( '404 Not Found' +
124
- '\n\n' + filePath +
125
- '\n\n(rollup-plugin-serve)' , 'utf-8' )
126
- }
127
-
128
- function found ( response , filePath , content ) {
129
- response . writeHead ( 200 , { 'Content-Type' : mime . getType ( filePath ) } )
130
- response . end ( content , 'utf-8' )
116
+ function errorPage ( req , res ) {
117
+ res . writeHead ( 404 )
118
+ res . end (
119
+ '404 Not Found' + '\n\n' + req . originalUrl + '\n\n(rollup-plugin-serve)' ,
120
+ 'utf-8'
121
+ )
122
+ }
131
123
}
132
124
133
- function green ( text ) {
125
+ function green ( text ) {
134
126
return '\u001b[1m\u001b[32m' + text + '\u001b[39m\u001b[22m'
135
127
}
136
128
@@ -146,8 +138,6 @@ function closeServerOnTermination() {
146
138
} )
147
139
}
148
140
149
- export default serve
150
-
151
141
/**
152
142
* @typedef {Object } ServeOptions
153
143
* @property {boolean } [open=false] Launch in browser (default: `false`)
0 commit comments