UNPKG

2.63 kBJavaScriptView Raw
1const fs = require('fs')
2const path = require('path')
3const { EventEmitter } = require('events')
4
5const mkdirp = require('mkdirp')
6const minimatch = require('minimatch')
7const babel = require('@babel/core')
8const chokidar = require('chokidar')
9
10const DEFAULT_OPTIONS = {
11 persistent: true,
12 delete: true,
13 babel: {},
14 glob: '**/*.js'
15}
16
17const SOURCE_MAP_PREFIX = '//# sourceMappingURL=data:application/json;base64,'
18
19const btoa = str => new Buffer(str, 'binary').toString('base64')
20
21class BabelCompiler extends EventEmitter {
22 constructor(src, dest, options) {
23 super()
24
25 const optionsWithDefault = Object.assign({}, DEFAULT_OPTIONS, options)
26
27 this.src = src
28 this.dest = dest
29 this.options = optionsWithDefault
30 this.ready = false
31
32 mkdirp.sync(dest)
33
34 const chokidarOptions = {
35 cwd: src,
36 persistent: optionsWithDefault.persistent
37 }
38
39 this.watcher = chokidar
40 .watch('**/*.*', chokidarOptions)
41 .on('all', (event, filepath) => this.onWatchEvent(event, filepath))
42 .on('error', e => this.onError(e))
43 .on('ready', () => this.onReady())
44 }
45
46 onWatchEvent(event, filepath = '.') {
47 const srcPath = path.join(this.src, filepath)
48 const destPath = path.join(this.dest, filepath)
49
50 if (event === 'add' || event === 'change') {
51 mkdirp.sync(path.dirname(destPath))
52 if (minimatch(filepath, this.options.glob)) {
53 this.compileJsFile(filepath, srcPath, destPath)
54 } else {
55 this.copyFile(filepath, srcPath, destPath)
56 }
57 }
58
59 if (event === 'unlink') {
60 if (this.options.delete) return
61 fs.removeSync(destPath)
62 this.emit('delete', filepath)
63 }
64
65 if (event === 'unlinkDir') {
66 if (!this.options.delete) return
67 fs.removeSync(destPath)
68 }
69 }
70
71 onReady() {
72 this.emit('ready')
73 }
74
75 onError(e) {
76 this.emit('error', e)
77 }
78
79 compileJsFile(filepath, srcPath, destPath) {
80 const babelOptions = this.options.sourceMaps
81 ? {
82 ...this.options.babel,
83 sourceMaps: true,
84 sourceFileName: srcPath
85 }
86 : this.options.babel
87
88 let result
89 try {
90 result = babel.transformFileSync(srcPath, babelOptions)
91 } catch (error) {
92 this.emit('error', filepath, error)
93 return
94 }
95
96 let content
97 if (this.options.sourceMaps) {
98 const sourceMap = btoa(JSON.stringify(result.map))
99 content = `${result.code}\n${SOURCE_MAP_PREFIX}${sourceMap}`
100 } else {
101 content = result.code
102 }
103
104 fs.writeFileSync(destPath, content)
105 this.emit('success', filepath)
106 }
107
108 copyFile(filepath, srcPath, destPath) {
109 fs.copyFileSync(srcPath, destPath)
110 this.emit('copy', filepath)
111 }
112
113 close() {
114 this.watcher.close()
115 this.removeAllListeners()
116 }
117}
118
119module.exports = BabelCompiler