1 | const path = require('path')
|
2 | const EventEmitter = require('events')
|
3 | const yargs = require('yargs')
|
4 | const webpack = require('webpack')
|
5 | const PostCompilePlugin = require('post-compile-webpack-plugin')
|
6 | const webpackMerge = require('webpack-merge')
|
7 | const rm = require('rimraf')
|
8 | const ware = require('ware')
|
9 | const merge = require('lodash/merge')
|
10 | const MemoryFS = require('memory-fs')
|
11 | const parsePresets = require('parse-json-config')
|
12 | const webpackUtils = require('./webpack-utils')
|
13 | const createConfig = require('./create-config')
|
14 | const createServer = require('./server')
|
15 | const { promisify, readPkg } = require('./utils')
|
16 |
|
17 | function runWebpack(compiler) {
|
18 | return new Promise((resolve, reject) => {
|
19 | compiler.run((err, stats) => {
|
20 | if (err) return reject(err)
|
21 | resolve(stats)
|
22 | })
|
23 | })
|
24 | }
|
25 |
|
26 | class Poi extends EventEmitter {
|
27 | constructor(options) {
|
28 | super()
|
29 | this.options = Object.assign({
|
30 | cwd: '.',
|
31 | argv: yargs.argv,
|
32 |
|
33 | host: process.env.HOST || '0.0.0.0',
|
34 | port: process.env.PORT || 4000
|
35 | }, options)
|
36 |
|
37 | this.manifest = readPkg()
|
38 | this.middlewares = []
|
39 | this.webpackFlows = []
|
40 |
|
41 | this.usePresets()
|
42 | this.addWebpackFlow(config => {
|
43 | config.plugin('compile-notifier')
|
44 | .use(PostCompilePlugin, [stats => {
|
45 | if (this.options.mode === 'development' || this.options.mode === 'watch') {
|
46 | this.emit('compile-done', stats)
|
47 | }
|
48 | }])
|
49 | })
|
50 | if (this.options.extendWebpack) {
|
51 | this.addWebpackFlow(this.options.extendWebpack)
|
52 | }
|
53 |
|
54 | this.webpackConfig = createConfig(this.options)
|
55 | this.webpackFlows.forEach(flow => flow(this.webpackConfig))
|
56 | }
|
57 |
|
58 | getWebpackConfig() {
|
59 | const config = this.webpackConfig.toConfig()
|
60 | if (this.options.webpack) {
|
61 | return typeof this.options.webpack === 'function' ?
|
62 | this.options.webpack(config) :
|
63 | webpackMerge(config, this.options.webpack)
|
64 | }
|
65 | return config
|
66 | }
|
67 |
|
68 | build() {
|
69 | return this.runMiddlewares()
|
70 | .then(() => {
|
71 | this.createCompiler()
|
72 | const { filename, path: outputPath } = this.compiler.options.output
|
73 |
|
74 | const implicitlyRemoveDist = this.options.removeDist !== false && /\[(chunk)?hash:?\d?\]/.test(filename)
|
75 | if (this.options.removeDist === true || implicitlyRemoveDist) {
|
76 | return promisify(rm)(path.join(outputPath, '*'))
|
77 | }
|
78 | })
|
79 | .then(() => runWebpack(this.compiler))
|
80 | }
|
81 |
|
82 | watch() {
|
83 | return this.runMiddlewares()
|
84 | .then(() => {
|
85 | this.createCompiler()
|
86 | return this.compiler.watch({}, () => {})
|
87 | })
|
88 | }
|
89 |
|
90 | dev() {
|
91 | return this.runMiddlewares()
|
92 | .then(() => {
|
93 | this.createCompiler()
|
94 | return createServer(this.compiler, this.options)
|
95 | })
|
96 | }
|
97 |
|
98 | test() {
|
99 | return this.runMiddlewares()
|
100 | }
|
101 |
|
102 | createCompiler(webpackConfig = this.getWebpackConfig()) {
|
103 | this.compiler = webpack(webpackConfig)
|
104 | if (this.options.inMemory) {
|
105 | this.compiler.outputFileSystem = new MemoryFS()
|
106 | }
|
107 | return this
|
108 | }
|
109 |
|
110 | addWebpackFlow(mode, fn) {
|
111 | if (typeof mode === 'function') {
|
112 | this.webpackFlows.push(mode)
|
113 | } else if (this.isMode(mode)) {
|
114 | this.webpackFlows.push(fn)
|
115 | }
|
116 | return this
|
117 | }
|
118 |
|
119 | addMiddleware(mode, fn) {
|
120 | if (typeof mode === 'function') {
|
121 | this.middlewares.push(mode)
|
122 | } else if (this.isMode(mode)) {
|
123 | this.middlewares.push(fn)
|
124 | }
|
125 | return this
|
126 | }
|
127 |
|
128 | isMode(mode) {
|
129 | const currentMode = this.options.mode
|
130 | const isWildcard = mode === '*'
|
131 | const isMode = typeof mode === 'string' && mode === currentMode
|
132 | const hasMode = Array.isArray(mode) && mode.indexOf(currentMode) > -1
|
133 | return isWildcard || isMode || hasMode
|
134 | }
|
135 |
|
136 | usePresets() {
|
137 | const presetContext = {
|
138 | isMode: this.isMode.bind(this),
|
139 | run: this.addMiddleware.bind(this),
|
140 | extendWebpack: this.addWebpackFlow.bind(this),
|
141 | options: this.options,
|
142 | argv: this.options.argv,
|
143 | manifest: this.manifest,
|
144 | webpackUtils,
|
145 | runWebpack,
|
146 | merge
|
147 | }
|
148 |
|
149 | let presets = this.options.presets
|
150 | if (presets) {
|
151 | if (typeof presets === 'string' || typeof presets === 'function') {
|
152 | presets = [presets]
|
153 | }
|
154 | presets = parsePresets(presets, {
|
155 | prefix: 'poi-preset-'
|
156 | })
|
157 | if (presets) {
|
158 | presets.forEach(preset => preset(presetContext))
|
159 | }
|
160 | }
|
161 | }
|
162 |
|
163 | runMiddlewares() {
|
164 | return new Promise((resolve, reject) => {
|
165 | ware()
|
166 | .use(this.middlewares)
|
167 | .run(this.getWebpackConfig(), err => {
|
168 | if (err) return reject(err)
|
169 | resolve()
|
170 | })
|
171 | })
|
172 | }
|
173 | }
|
174 |
|
175 | module.exports = options => new Poi(options)
|