UNPKG

4.73 kBJavaScriptView Raw
1const path = require('path')
2const EventEmitter = require('events')
3const yargs = require('yargs')
4const webpack = require('webpack')
5const PostCompilePlugin = require('post-compile-webpack-plugin')
6const webpackMerge = require('webpack-merge')
7const rm = require('rimraf')
8const ware = require('ware')
9const merge = require('lodash.merge')
10const MemoryFS = require('memory-fs')
11const parsePresets = require('parse-json-config')
12const webpackUtils = require('./webpack-utils')
13const createConfig = require('./create-config')
14const createServer = require('./server')
15const { promisify, readPkg } = require('./utils')
16
17function 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
26class Poi extends EventEmitter {
27 constructor(options) {
28 super()
29 this.options = Object.assign({
30 cwd: '.',
31 argv: yargs.argv,
32 // Required for cloud IDE like cloud9
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 // Only remove dist file when name contains hash
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.webpackConfig, err => {
168 if (err) return reject(err)
169 resolve()
170 })
171 })
172 }
173}
174
175module.exports = options => new Poi(options)