werkzeug
Version:
compiles ts, coffee, sass/scss, less/ stylus and packs browser ready bundles
282 lines (208 loc) • 6.97 kB
text/coffeescript
Emitter = require 'events'
FS = require 'fs'
Path = require 'path'
Config = require './config'
Server = require './server'
Walker = require './walker'
Watcher = require './watcher'
Compiler = require './compiler'
Packer = require './packer'
FSU = require './utils/fsu'
PH = require './utils/path-helper'
Log = require('./utils/log').mapLogs()
#TODO: add multiple in and out paths!!!
#TODO: refactor path-helper somehow in cfg!!!
#TODO: add post processors (like autoprefixer)!!!
#TODO: fix errors for empty project!!!
#TODO: refactor error handling -> use info object???
#TODO: fix errors for multiple instances in the same project
#TODO: set ts.noEmitOnError = true and merge packer errors back to compiler errors???
#TODO: make source maps optional!!!
#TODO: enable cli flag for different configs, maybe run configs concurrently
#TODO: watch current wz config and restart app on change
#TODO: remember TREESHAKING for the build process (if it ever comes)???
#TODO: implement salter???
class Werkzeug extends Emitter
@ignores = 'node_modules$|\\.DS_Store$|dist$|\\.idea$|\\.git$|gulpfile\\.coffee$|Gruntfile\\.coffee$'
@interests = '\\.coffee$|\\.ts$|\\.sass$|\\.scss$|\\.less$|\\.styl$'
@updateDelay = 500
constructor: (base, cfg) ->
super()
Log()
Log.info 'werkzeug', 'starting ...'
Log.startTicker 'werkzeug starting'
@cfg = new Config(base, cfg)
@walker = new Walker (@)
@watcher = new Watcher (@)
@compiler = new Compiler(@)
@packer = new Packer (@) if @cfg.packer.enabled and @cfg.out
@server = new Server (@) if @cfg.server.enabled and @cfg.out
@inDir = Path.join @cfg.base, @cfg.in
@errors = []
@files = []
@fileMap = {}
@idle = true
@initialized = false
@dirty = false
# clear all output paths and add them to ignores
paths = PH.getPaths(@cfg)
for path in paths
FSU.rmDir path if FSU.isDir path
FS.mkdirSync path
ignore = Path.relative @cfg.base, path
ignore = ignore.replace(/\\/g, '\\\\').replace(/\//g, '\\/').replace(/\./g, '\\.') + '$'
WZ.ignores += '|' + ignore
@ignores = new RegExp("(#{WZ.ignores})")
@interests = new RegExp("(#{WZ.interests})")
process.on 'exit', @terminate
process.on 'SIGINT', @terminate
process.on 'SIGTERM', @terminate
watch: () ->
return if not @idle or @watcher.watching
#TODO: make server optional
@server.init() if @server
@watcher.watch()
null
walk: () ->
return if not @idle
@idle = false
@walker.walk()
null
walked: () ->
@idle = true
@compile()
null
compile: () ->
return if not @idle
if @initialized
Log.startTicker 'compiling'
else
Log.setTicker 'compiling'
@idle = false
compiling = @compiler.compile()
@cleanFiles()
@dirty = false
if not compiling
@idle = true
if not @watcher.watching
@pack()
else if not @initialized or not @packer
@packed()
null
compiled: () ->
@idle = true
if not @compiler.errors.length
@pack()
else
@packed()
null
pack: () ->
return if not @idle
@idle = false
if @packer
Log.setTicker 'packing'
@packer.pack @compiler.files
else
@packed()
null
packed: () ->
@idle = true
t = Log.stopTicker()
e = @compiler.errors.concat (@packer?.errors or [])
el = e.length
wl = @compiler.warnings.length
w = @watcher.watching
@compiler.logErrors()
@packer.logErrors() if @packer
if el + wl
console.log ''
if not @initialized
@initialized = true
s = if w then "startup" else "single run"
else if @compiler.files and @compiler.files.length
s = "ready"
Log.info('werkzeug', s, t, el) if s
if w
if @dirty
@update()
else if @compiler.files and @compiler.files.length
now = new Date()
h = now.getHours()
m = now.getMinutes()
s = now.getSeconds()
h = '0' + h if h < 10
m = '0' + m if m < 10
s = '0' + s if s < 10
Log.info 'werkzeug', 'watching' + ' ... ' + (h + ':' + m + ':' + s + ' - ' + @cfg.base)
else
@terminate()
null
update: () ->
@dirty = true
return if not @idle or not @initialized
clearTimeout @updateTimeout
@updateTimeout = setTimeout @updateNow, WZ.updateDelay
null
updateNow: () =>
return if not @idle
@compile()
null
cleanFiles: () ->
@errors = []
for path, file of @fileMap
if file.dirty
if file.removed
index = @files.indexOf file
@files.splice index, 1 if index > -1
delete @fileMap[file.path]
else
file.dirty = false
file.added = false
file.changed = false
file.errors = null
null
fileAdded: (path) ->
return null if path.indexOf(@inDir) != 0
file = @fileMap[path]
if file
file.removed = false
else
@files.push(file = @fileMap[path] = {})
file.path = path
file.added = Date.now()
file.dirty = true
file.errors = null
@update()
#console.log 'fileAdded: ', path
null
fileChanged: (path) ->
file = @fileMap[path]
return if not file
file.changed = Date.now()
file.dirty = true
file.errors = null
@update()
#console.log 'fileChanged: ', path
null
fileRemoved: (path) ->
file = @fileMap[path]
return if not file
file.removed = Date.now()
file.dirty = true
@update()
#console.log 'fileRemoved: ', path
null
ignore: (path) ->
@ignores.test path
interested: (path) ->
@interests.test path
terminate: () =>
Log()
clearTimeout @updateTimeout
@compiler.exit()
@packer.exit() if @packer
@server.exit() if @server
process.removeAllListeners()
setTimeout () -> process.exit 0
null
module.exports = WZ = Werkzeug