-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
/
index.ts
198 lines (181 loc) · 5.46 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import path from 'path'
import fs from 'fs-extra'
import { RequestListener, Server } from 'http'
import { ServerOptions } from 'https'
import Koa, { DefaultState, DefaultContext } from 'koa'
import chokidar from 'chokidar'
import { createResolver, InternalResolver } from '../resolver'
import { moduleRewritePlugin } from './serverPluginModuleRewrite'
import { moduleResolvePlugin } from './serverPluginModuleResolve'
import { vuePlugin } from './serverPluginVue'
import { hmrPlugin, HMRWatcher } from './serverPluginHmr'
import { serveStaticPlugin } from './serverPluginServeStatic'
import { jsonPlugin } from './serverPluginJson'
import { cssPlugin } from './serverPluginCss'
import { assetPathPlugin } from './serverPluginAssets'
import { esbuildPlugin } from './serverPluginEsbuild'
import { ServerConfig } from '../config'
import { createServerTransformPlugin } from '../transform'
import { htmlRewritePlugin } from './serverPluginHtml'
import { proxyPlugin } from './serverPluginProxy'
import { createCertificate } from '../utils/createCertificate'
import { cachedRead, toArray } from '../utils'
import { envPlugin } from './serverPluginEnv'
export { rewriteImports } from './serverPluginModuleRewrite'
import { sourceMapPlugin, SourceMap } from './serverPluginSourceMap'
import { webWorkerPlugin } from './serverPluginWebWorker'
import { wasmPlugin } from './serverPluginWasm'
import { clientPlugin } from './serverPluginClient'
import { AddressInfo } from 'net'
export type ServerPlugin = (ctx: ServerPluginContext) => void
export interface ServerPluginContext {
root: string
app: Koa<State, Context>
server: Server
watcher: HMRWatcher
resolver: InternalResolver
config: ServerConfig & { __path?: string }
port: number
}
export interface State extends DefaultState {}
export type Context = DefaultContext &
ServerPluginContext & {
read: (filePath: string) => Promise<Buffer | string>
map?: SourceMap | null
}
export function createServer(config: ServerConfig): Server {
const {
root = process.cwd(),
configureServer = [],
resolvers = [],
alias = {},
transforms = [],
vueCustomBlockTransforms = {},
optimizeDeps = {},
enableEsbuild = true,
assetsInclude
} = config
const app = new Koa<State, Context>()
const server = resolveServer(config, app.callback())
const watcher = chokidar.watch(root, {
ignored: [/node_modules/, /\.git/],
// #610
awaitWriteFinish: {
stabilityThreshold: 100,
pollInterval: 10
}
}) as HMRWatcher
const resolver = createResolver(root, resolvers, alias, assetsInclude)
const context: ServerPluginContext = {
root,
app,
server,
watcher,
resolver,
config,
// port is exposed on the context for hmr client connection
// in case the files are served under a different port
port: config.port || 3000
}
// attach server context to koa context
app.use((ctx, next) => {
Object.assign(ctx, context)
ctx.read = cachedRead.bind(null, ctx)
return next()
})
// cors
if (config.cors) {
app.use(
require('@koa/cors')(typeof config.cors === 'boolean' ? {} : config.cors)
)
}
const resolvedPlugins = [
// rewrite and source map plugins take highest priority and should be run
// after all other middlewares have finished
sourceMapPlugin,
moduleRewritePlugin,
htmlRewritePlugin,
// user plugins
...toArray(configureServer),
envPlugin,
moduleResolvePlugin,
proxyPlugin,
clientPlugin,
hmrPlugin,
...(transforms.length || Object.keys(vueCustomBlockTransforms).length
? [
createServerTransformPlugin(
transforms,
vueCustomBlockTransforms,
resolver
)
]
: []),
vuePlugin,
cssPlugin,
enableEsbuild ? esbuildPlugin : null,
jsonPlugin,
assetPathPlugin,
webWorkerPlugin,
wasmPlugin,
serveStaticPlugin
]
resolvedPlugins.forEach((m) => m && m(context))
const listen = server.listen.bind(server)
server.listen = (async (port: number, ...args: any[]) => {
if (optimizeDeps.auto !== false) {
await require('../optimizer').optimizeDeps(config)
}
return listen(port, ...args)
}) as any
server.once('listening', () => {
context.port = (server.address() as AddressInfo).port
})
return server
}
function resolveServer(
{ https = false, httpsOptions = {}, proxy }: ServerConfig,
requestListener: RequestListener
): Server {
if (!https) {
return require('http').createServer(requestListener)
}
if (proxy) {
// #484 fallback to http1 when proxy is needed.
return require('https').createServer(
resolveHttpsConfig(httpsOptions),
requestListener
)
} else {
return require('http2').createSecureServer(
{
...resolveHttpsConfig(httpsOptions),
allowHTTP1: true
},
requestListener
)
}
}
function resolveHttpsConfig(httpsOption: ServerOptions) {
const { ca, cert, key, pfx } = httpsOption
Object.assign(httpsOption, {
ca: readFileIfExists(ca),
cert: readFileIfExists(cert),
key: readFileIfExists(key),
pfx: readFileIfExists(pfx)
})
if (!httpsOption.key || !httpsOption.cert) {
httpsOption.cert = httpsOption.key = createCertificate()
}
return httpsOption
}
function readFileIfExists(value?: string | Buffer | any) {
if (value && !Buffer.isBuffer(value)) {
try {
return fs.readFileSync(path.resolve(value as string))
} catch (e) {
return value
}
}
return value
}