UNPKG

52.5 kBtext/coffeescriptView Raw
1do ->
2 require('dotenv').load silent: true
3 process.env.NODE_ENV = process.env.APP_ENV or process.env.NODE_ENV or 'local'
4
5Path = require 'path'
6argv = require('optimist').argv
7
8cached = (fn) ->
9 fn.cache ?= {}
10 return (name) ->
11 unless fn.cache[name]
12 fn.cache[name] = fn(name)
13 return fn.cache[name]
14
15loader = cached (module) ->
16 if module is 'lo'
17 m = require 'lodash'
18 else if module is 'gulpif'
19 m = require 'gulp-if'
20 else if module is 'rename'
21 m = require 'gulp-rename'
22 else if module is 'runSequence'
23 m = require 'run-sequence'
24 else if module is 'streamify'
25 m = require 'gulp-streamify'
26 else if module is 'through'
27 m = require 'through2'
28 else if module is 'concat'
29 m = require 'gulp-concat'
30 else if module is 'newer'
31 m = require 'gulp-newer'
32 else if module is 'coffee'
33 m = require 'gulp-coffee'
34 else if module is 'less'
35 m = require 'gulp-less'
36 else if module is 'autoprefixer'
37 m = require 'gulp-autoprefixer'
38 else if module is 'cssmin'
39 m = require 'gulp-cssmin'
40 else if module is 'uglify'
41 m = require 'gulp-uglify'
42 else if module is 'sass'
43 m = require 'gulp-sass'
44 else if module is 'browserifyInc'
45 m = require 'browserify-incremental'
46 else if module is 'envify'
47 m = require 'envify/custom'
48 else if module is 'coffeeify'
49 m = require 'coffee-reactify'
50 else if module is 'livereload'
51 m = require 'gulp-livereload'
52 else if module is 'plumber'
53 m = require 'gulp-plumber'
54 else if module is 'ngAnnotate'
55 m = require 'gulp-ng-annotate'
56 else if module is 'Promise'
57 m = require 'promise'
58 require('promise/lib/rejection-tracking').enable(allRejections: true)
59 else if module is 'notifier'
60 m = require 'node-notifier'
61 else if module is 'Hjson'
62 m = require 'hjson'
63 else if module is 'sourcemaps'
64 m = require 'gulp-sourcemaps'
65 else if module is 'applySourceMap'
66 m = require 'vinyl-sourcemaps-apply'
67 else if module is 'colors'
68 m = require 'chalk'
69 else if module is 'babel'
70 m = require 'babel-core'
71 else if module is 'typecheck'
72 m = require('babel-plugin-typecheck').default
73 else if module is 'babelInlineEnv'
74 m = require 'babel-plugin-transform-inline-environment-variables'
75 else if module is 'reactPreset'
76 m = require 'babel-preset-react'
77 else if module is 'babelDecorators'
78 require('babel-template')
79 m = require('babel-plugin-transform-decorators-legacy').default
80 else if module is 'babelClassProperties'
81 m = require 'babel-plugin-transform-class-properties'
82 else if module is 'es2015Preset'
83 # {buildPreset} = require 'babel-preset-es2015'
84 # m = buildPreset {}, loose: true
85 cwd = process.cwd
86 process.cwd = -> __dirname
87 m = require 'babel-preset-es2015-loose'
88 process.cwd = cwd
89 else
90 m = require module
91 return m
92
93getManifest = (manifestPath) ->
94 return try
95 require manifestPath
96 catch error
97 {}
98
99log = ->
100 if argv.verbose
101 console.log arguments...
102
103only = (extensions...) ->
104 through = loader 'through'
105
106 return through.obj (file, enc, callback) ->
107 if file.isNull()
108 callback null, file
109 return
110 else if file.isStream()
111 return throw new Error('Streams are not supported!')
112
113 isIt = false
114 for ext in extensions
115 if Path.extname(file.path) is ext
116 isIt = true
117 break
118
119 if isIt
120 callback null, file
121 else
122 callback()
123
124isRawFile = (file) ->
125 return file.indexOf('//') is 0 or file.indexOf('://') > 0
126
127excludeRawFiles = (files) ->
128 f = []
129 if files?.length > 0
130 for file in files
131 unless isRawFile file
132 f.push file
133 return f
134
135hashify = ->
136 through = loader 'through'
137 return through.obj (file, enc, callback) ->
138 if file.isNull()
139 callback null, file
140 return
141 else if file.isStream()
142 return throw new Error('Streams are not supported!')
143
144 if file.shad?
145 file.path = Path.join Path.dirname(file.path), file.shad
146 callback null, file
147
148logFile = (log) ->
149 through = loader 'through'
150 return through.obj (file, enc, callback) ->
151 if log then console.log file.path
152 callback null, file
153
154normalizePath = (path) ->
155 normalizePath.cache[path] ?= path.replace /\//g, Path.sep
156 normalizePath.cache[path]
157
158normalizePath.cache = {}
159
160removeDirs = (path, dirs) ->
161 dirs.sort (a, b) ->
162 al = a?.length or 0
163 bl = b?.length or 0
164 bl - al
165
166 path = normalizePath path
167
168 for dir in dirs
169 dir = normalizePath dir
170
171 if path.indexOf(dir) is 0
172 path = path.replace(dir, '')
173 break
174
175 if path[0] is '/' or path[0] is '\\'
176 path = path.substring(1)
177
178 return path
179
180fixSlashes = (path) ->
181 regex = /\\/g
182 path.replace regex, '/'
183
184isLess = (file) ->
185 Path.extname(file.path) is '.less'
186
187isSass = (file) ->
188 Path.extname(file.path) is '.scss'
189
190isCoffee = (file) ->
191 return Path.extname(file.path) is '.coffee'
192
193checkExt = (path, extensions)->
194 for ext in extensions
195 if path.indexOf(ext) is path.length - ext.length
196 return true
197 false
198
199isStyle = (path) -> checkExt path, ['.less', '.scss', '.coffee']
200
201isScript = (path) -> checkExt path, ['.js', '.coffee', '.ts']
202
203unrelativize = ->
204 through = loader 'through'
205 through.obj (file, enc, callback) ->
206 regex = /\.\.[\\\/]/g
207 match = file.relative.match regex
208 if /^\.\.[\\\/]/g.test(file.relative) and match
209 base = Path.resolve('.')
210 for m in match
211 base = Path.resolve base + '/..'
212 file.base = base
213 callback null, file
214
215passNext = ->
216 through = loader 'through'
217 streamify = loader 'streamify'
218 streamify through.obj (file, enc, callback) ->
219 if file.isNull()
220 callback null, file
221 return
222 else if file.isStream()
223 return throw new Error('Streams are not supported!')
224 callback null, file
225
226isBowerComponent = (file) -> return file.indexOf('bower_components') >= 0
227
228shad = (file, ext) ->
229 crypto = loader 'crypto'
230 shasum = crypto.createHash('sha1')
231 shasum.update(file.contents)
232 sha = shasum.digest('hex')
233 file.originalPath = file.path + ''
234 Path.basename(file.path, Path.extname(file.path)) + '-' + sha + '.' + ext
235
236# normalizeDotPaths = (name) ->
237normalizeDotPaths = ->
238 through = loader 'through'
239 through.obj (file, enc, callback) ->
240 dirname = Path.dirname(file.path)
241 extname = Path.extname(file.path)
242 basename = Path.basename(file.path, extname)
243 dirname = dirname.replace /\./g, '-'
244 file.path = Path.join(dirname, basename) + extname
245 callback null, file
246
247class TransformCache
248
249 constructor: ->
250 @saveCache = {}
251 @manifestPath = ''
252
253 load: (@manifestPath) ->
254 lo = loader 'lo'
255 fs = loader 'fs'
256 @saveCache = lo.extend {}, try
257 JSON.parse fs.readFileSync(@manifestPath)
258 catch error
259 {}
260
261 save: ->
262 if @dirty
263 log "Writing transform cache"
264 process.nextTick =>
265 @cleanUp(@saveCache)
266 fs = loader 'fs'
267 fs.writeFile @manifestPath, JSON.stringify(@saveCache, null, "\t")
268
269 cleanUp: (saveCache) ->
270 now = new Date().getTime()
271 months_ago = now - 3*30*24*60*60*1000
272
273 loopSingle = (name, multiple) ->
274 for sha, def of multiple
275 if def.timeStamp > months_ago
276 delete multiple[sha]
277
278 for name, multiple of saveCache
279 loopSingle name, multiple
280
281 notProcessed: (file) ->
282 file._processed isnt true
283
284 fill: (name) ->
285 @saveCache[name] ?= {}
286 through = loader 'through'
287 streamify = loader 'streamify'
288 crypto = loader 'crypto'
289
290 streamify through.obj (file, enc, callback) =>
291 if file.isNull()
292 callback null, file
293 return
294 else if file.isStream()
295 return throw new Error('Streams are not supported!')
296
297 shasum = crypto.createHash('sha1')
298 shasum.update(file.contents)
299 sha = shasum.digest('hex')
300
301 if @saveCache[name][sha]
302 contents = @saveCache[name][sha]?.contents ? @saveCache[name][sha]
303
304 file.contents = new Buffer(contents)
305 file._processed = true
306
307 file._sha = sha
308 callback null, file
309
310 add: (name) ->
311 @saveCache[name] ?= {}
312 through = loader 'through'
313 streamify = loader 'streamify'
314
315 streamify through.obj (file, enc, callback) =>
316 if file.isNull()
317 callback null, file
318 return
319 else if file.isStream()
320 return throw new Error('Streams are not supported!')
321
322 unless @saveCache[name][file._sha]
323 @dirty = true
324 @saveCache[name][file._sha] = {
325 contents: file.contents.toString(enc)
326 timestamp: new Date().getTime()
327 }
328
329 callback(null, file)
330
331
332class Event
333 constructor: (@data) ->
334 @prevented = false
335
336 preventDefault: ->
337 @prevented = true
338
339 isDefaultPrevented: -> @prevented is true
340
341
342class Basset
343
344 constructor: (options = {}) ->
345 @lodash = options.lodash or do -> loader 'lo'
346 @Hjson = options.Hjson or do -> loader 'Hjson'
347 @livereload = options.Hjson or do -> loader 'livereload'
348 @reset()
349
350 reset: ->
351 @collections = {}
352 @bundles = {}
353 @config = {
354 dest: 'public/builds'
355 manifest: 'manifest.json'
356 basePaths: [
357 'resources/assets'
358 'assets'
359 ]
360 };
361 @localEnvs = [
362 'local'
363 'dev'
364 'development'
365 ]
366 @loadedDefinitions = []
367 @pipes = {}
368 @manifest = getManifest Path.resolve @config.dest + '/' + @config.manifest
369
370 loadFile: (file, config = {}) ->
371 fs = loader 'fs'
372 Hjson = loader 'Hjson'
373 EventEmitter = loader 'events'
374
375 @events = new EventEmitter()
376
377 if config.gulp
378 @gulp = config.gulp
379 else
380 @gulp = loader 'gulp'
381
382 collectionsJsonDefinition = Path.resolve file
383 contents = fs.readFileSync(collectionsJsonDefinition).toString()
384 json = Hjson.parse(contents)
385 @scan(json, config, file)
386
387 writeManifest: ->
388 fs = loader 'fs'
389
390 @manifest.environment = if @isProduction() then 'production' else 'local'
391 json = JSON.stringify(@manifest, null, ' ')
392 fs.writeFileSync @config.dest + '/' + @config.manifest, json
393
394 findCachesPath: ->
395 fs = loader 'fs'
396 path = ''
397 frameworkPath = 'storage/framework/cache'
398
399 if fs.existsSync(frameworkPath)
400 frameworkPath += Path.sep + 'basset'
401 if not fs.existsSync(frameworkPath)
402 fs.mkdirSync(frameworkPath)
403
404 return if fs.existsSync(frameworkPath)
405 Path.resolve(frameworkPath)
406 else
407 Path.resolve(@config.dest)
408
409 putIntoManifest: (file, name, ext, callback) =>
410 unless file.originalPath?
411 throw new Error "File doesn't have a originalPath property #{file.path}"
412
413 lo = loader 'lo'
414 pathResolve = Path.resolve(file.originalPath)
415
416 dirs = lo.extend [], @config.baseDirsRealpaths
417 if @isProduction()
418 dirs.push Path.resolve(@config.dest)
419
420 original = removeDirs pathResolve, dirs
421
422 builds = Path.resolve @config.dest
423 resolved = file.path.replace builds, ''
424
425 env = if @isProduction() then 'production' else 'local'
426
427 @manifest[name] ?= {}
428 @manifest[name][env] ?= {}
429 @manifest[name][env][ext] ?= []
430 @manifest[name][env][ext][original] = resolved
431 callback()
432
433 finalizeManifest: (name, ext) ->
434 through = loader 'through'
435 lo = loader 'lo'
436 return through.obj (file, enc, callback) =>
437 if file.isNull()
438 callback null, file
439 return
440 else if file.isStream()
441 return throw new Error('Streams are not supported!')
442
443 if lo.endsWith(file.path, '.map')
444 callback null, file
445 return
446
447 @putIntoManifest file, name, ext, ->
448 callback null, file
449
450 isProduction: ->
451 return process.env.NODE_ENV not in @localEnvs
452
453 scan: (collectionsJson, configPassed = {}, collectionFile) ->
454 pipes = @pipes
455 lo = @lodash
456 gulp = @gulp
457 runSequence = loader('runSequence').use(gulp)
458 self = this
459
460 if collectionFile not in @loadedDefinitions
461 @loadedDefinitions.push collectionFile
462
463 @config = lo.extend(@config, configPassed)
464
465 for name, definition of collectionsJson.collections
466 @collections[name] = definition
467
468 if collectionsJson.basePaths?
469 for path in collectionsJson.basePaths
470 path = Path.dirname(Path.resolve(collectionFile)) + '/' + path
471 relativePath = './' + Path.relative Path.resolve('.'), path
472 @config.basePaths.push relativePath
473
474 isProduction = @isProduction.bind(@)
475
476 filters = @config.filters = lo.extend({
477 less:
478 paths: [Path.join(__dirname, 'less', 'includes')]
479
480 livereload:
481 port: process.env?.LIVERELOAD_PORT or 35729
482
483 sourcemaps: {}
484
485 babelify: ->
486 options = plugins: [], presets: []
487
488 options.presets.push loader 'es2015Preset'
489 options.presets.push loader 'reactPreset'
490
491 options.plugins.push loader 'babelDecorators'
492 options.plugins.push loader 'babelInlineEnv'
493
494 options.plugins.push loader 'babelClassProperties'
495
496 unless isProduction()
497 options.plugins.push loader 'typecheck'
498
499 self.events.emit('babel:options')
500
501 return options
502
503 watchify:
504 watch: false
505 setup: (bundle) ->
506 coffeeify = loader 'coffeeify'
507 babelify = loader 'babelify'
508 fs = loader 'fs'
509 brfs = loader 'brfs'
510 envify = loader 'envify'
511 uglifyify = loader 'uglifyify'
512
513 self.events.emit 'browserify:before-transforms', bundle
514
515 bundle.transform(coffeeify)
516 bundle.transform(babelify.configure(filters.babelify()))
517 bundle.transform(brfs)
518
519 self.events.emit 'browserify:after-transforms', bundle
520
521 if isProduction()
522 self.events.emit 'browserify:before-production-transforms', bundle
523
524 sourcemap = checkSourceMaps() is true
525 bundle.transform envify({_: 'purge'})
526 options = {global: true, mangle: false, sourcemap}
527 options.output = {
528 comments: true
529 }
530 options.compress = {
531 angular: true
532 }
533 bundle.transform options, uglifyify
534
535 self.events.emit 'browserify:after-production-transforms', bundle
536
537 self.events.emit 'browserify:after-setup', bundle
538
539 bundle
540
541 extensions: '.coffee'
542
543 autoprefixer:
544 browsers: ['last 20 versions']
545 cascade: true
546
547 cssmin: {}
548
549 uglify: {}
550
551 sass: null
552
553 ngAnnotate:
554 remove: false
555 add: true
556 single_quotes: true
557 }, @config.filters or {})
558
559 CACHES_PATH = @findCachesPath()
560 @config.baseDirsGlob = '{' + @config.basePaths.join(',') + '}'
561 @config.baseDirsRealpaths = []
562
563 baseDirsGlob = @config.baseDirsGlob
564 baseDirsRealpaths = @config.baseDirsRealpaths
565
566 transformCache = null
567
568 envCache = ->
569 crypto = loader 'crypto'
570
571 envCache.hash ?= crypto.createHash 'md5'
572 .update JSON.stringify process.env
573 .digest 'hex'
574 envCache.hash
575
576
577 initTransformCache = ->
578 unless transformCache?
579 envHash = envCache()
580 transformCache = new TransformCache
581 transformCache.load CACHES_PATH + "/basset-cache-#{envHash}.tmp"
582
583 scanBasedirs = ->
584 Promise = loader 'Promise'
585 glob = loader 'glob'
586
587 return scanBasedirs.scanPromise if scanBasedirs.scanPromise?
588 scanBasedirs.scanPromise = new Promise (done) ->
589 glob baseDirsGlob, (err, dirs) ->
590 baseDirsRealpaths[k] = Path.resolve(v) for v, k in dirs
591 done()
592
593 checkSourceMaps = -> true if filters.sourcemaps?.write is true
594
595 handleError = (err) ->
596 for d in baseDirsRealpaths
597 regex = new RegExp(d + '/', 'g')
598 err.message = err.message.replace(regex, '')
599
600 if argv.verbose or not filters.watchify.watch
601 console.log err.message, err.stack
602 else
603 console.log err.message
604
605 if err.extract?
606 console.log err.extract.join("\n")
607
608 if err._babel
609 err.excerpt = err.codeFrame
610 console.log err.excerpt
611
612 message = err.message
613
614 if err.extract
615 message += "\n" + err.extract.join("\n")
616
617 notifier = loader 'notifier'
618 beeper = loader 'beeper'
619
620 event = new Event(err)
621 self.events.emit 'build:error', event
622
623 unless event.isDefaultPrevented()
624 notifier.notify(
625 title: 'Basset - Build Error',
626 message: message
627 sound: true
628 time: 30 * 1000
629 , (err) ->
630 beeper() if err
631 )
632
633 unless filters.watchify.watch
634 e = new Event(err)
635 self.events.emit('build:error-exit', e)
636
637 unless e.isDefaultPrevented()
638 process.exit(1)
639
640 @emit?('end')
641 return
642
643 buildsCount = 0
644
645 blockLivereload = false
646
647 livereload = loader 'livereload'
648
649 livereloadWildcard = =>
650 if buildsCount and not blockLivereload
651 event = new Event({path:'*'})
652 @events.emit('reload', event)
653 unless event.isDefaultPrevented()
654 livereload.changed('*')
655
656 livereloadPath = (path) =>
657 if buildsCount and not blockLivereload
658 event = new Event({path})
659 @events.emit('reload', event)
660 unless event.isDefaultPrevented()
661 livereload.changed(path)
662
663 debounceReload = lo.debounce livereloadWildcard, 150
664
665 livereloadPipe = ->
666 through = loader 'through'
667 through.obj (file, enc, callback) ->
668 if filters.watchify.watch
669 if file.shad?
670 debounceReload()
671 else
672 livereloadPath(file.path)
673 callback null, file
674
675 @events.on 'livereload:change', (args) ->
676 livereload.changed args...
677
678 prefixWithBase = (name, files) ->
679 prefixed = []
680 for f in files
681 if f[0] is '!'
682 for dir in baseDirsRealpaths
683 prefixed.push '!' + dir + '/' + f.substring(1)
684 else
685 prefixed.push(baseDirsGlob + '/' + name + '/' + f)
686
687 return prefixed
688
689 swapWithMinFile = =>
690 through = loader 'through'
691 through.obj (file, enc, callback) =>
692 if file.isNull()
693 callback null, file
694 return
695 else if file.isStream()
696 return throw new Error('Streams are not supported!')
697
698 dirname = Path.dirname file.path
699 extname = Path.extname file.path
700 basename = Path.basename file.path, extname
701 minFile = Path.join(dirname, basename) + '.min' + extname
702
703 fs = loader 'fs'
704 colors = loader 'colors'
705
706 fs.exists minFile, (exists) =>
707 return callback null, file if not exists
708
709 console.log colors.cyan('Discovered'), removeDirs minFile, @config.baseDirsRealpaths
710
711 fs.readFile minFile, (err, contents) ->
712 return handleError(err) if err
713 file.contents = contents
714 callback null, file
715
716 force = false
717
718 globCheckSkipClean = (name, ext) =>
719 through = loader 'through'
720 return through.obj (file, enc, callback) =>
721 if file.isNull()
722 callback null, file
723 return
724 else if file.isStream()
725 return throw new Error('Streams are not supported!')
726
727 glob = loader 'glob'
728 fs = loader 'fs'
729
730 file.originalPath = file.path + ''
731
732 if ext is 'css' and not isProduction()
733 hashGlob = do =>
734 fileWithStar = Path.dirname(file.path) + '/' + Path.basename(file.path, Path.extname(file.path)) + '.' + ext
735 withoutRealpaths = removeDirs fileWithStar, baseDirsRealpaths
736 return @config.dest + "/#{name}/#{baseDirsGlob}/" + withoutRealpaths
737
738
739 glob hashGlob, {}, (err, files) =>
740 exists = false
741 files.forEach (filePath) ->
742 exists = filePath
743 return false
744
745 if exists and force isnt true
746 fs.stat exists, (err, stat) =>
747 if err
748 return handleError err
749
750 if file.stat.mtime.getTime() > stat.mtime.getTime()
751 callback null, file
752 else
753 log 'Skiping', file.path
754
755 fakeFile = {
756 path: Path.resolve(exists)
757 originalPath: file.originalPath
758 }
759
760 @putIntoManifest fakeFile, name, ext, ->
761 callback()
762 else
763 callback null, file
764 return
765
766
767 if file.shad?
768 throw new Error('Running shad twice?')
769
770 file.shad = shad file, ext
771
772 hashGlob = do =>
773 fileWithStar = Path.dirname(file.path) + '/' + Path.basename(file.path, Path.extname(file.path)) + '-*' + '.{' + ext + ',' + ext + '.map}'
774
775 dirs = lo.extend [], baseDirsRealpaths
776 if isProduction()
777 dirs.push Path.resolve(@config.dest)
778
779 withoutRealpaths = removeDirs fileWithStar, dirs
780
781 if isProduction()
782 return @config.dest + '/' + withoutRealpaths
783
784 return @config.dest + "/#{name}/#{baseDirsGlob}/" + withoutRealpaths
785
786 regex = do =>
787 fileWithStar = Path.dirname(file.path) + '/' + Path.basename(file.path, Path.extname(file.path)) + '-[0-9a-f]{5,40}' + '.(' + ext + '|' + ext+ '.map)$'
788
789 dirs = lo.extend [], baseDirsRealpaths
790 if isProduction()
791 dirs.push Path.resolve(@config.dest)
792
793 withoutRealpaths = removeDirs fileWithStar, dirs
794
795 if isProduction()
796 return @config.dest + '/' + withoutRealpaths
797 return withoutRealpaths
798
799 glob hashGlob, {}, (err, files) =>
800 exists = false
801
802 files.forEach (filePath) ->
803 resolvedBase = Path.basename(filePath)
804
805 if force isnt true and resolvedBase is file.shad
806 log 'Skiping', file.path
807 exists = filePath
808 else
809 expression = new RegExp(regex, 'gi')
810
811 if expression.test filePath
812 log 'Deleting', filePath
813 fs.unlinkSync(filePath)
814
815 if exists
816 file.path = Path.resolve(exists)
817
818 @putIntoManifest file, name, ext, ->
819 callback()
820 else
821 callback null, file
822
823 watchStreams = []
824
825 modulesRegex = "(require(\\s+|\\()|import\\b|export\\b)"
826
827 browserifyBundle = (name) =>
828 through = loader 'through'
829 return through.obj (file, enc, callback) =>
830 if file.isNull()
831 callback null, file
832 return
833 else if file.isStream()
834 return throw new Error('Streams are not supported!')
835
836 contents = file.contents.toString(enc)
837 regex = new RegExp modulesRegex, 'g'
838
839 if not regex.test contents
840 file.contentsToString = contents
841 return callback null, file
842
843 file.isBundle = true
844
845 file.originalPath = file.path + ''
846
847 bundler = @bundles[file.path]
848
849 unless bundler
850 opts =
851 debug: not isProduction()
852
853 if checkSourceMaps()
854 opts.debug = true
855
856 envHash = envCache()
857 browserifyCacheFile = CACHES_PATH + "/browserify-cache-#{name}.#{envHash}.tmp"
858
859 browserify = loader 'browserify'
860 browserifyInc = loader 'browserifyInc'
861
862 browserifyOptions = {
863 entries: [file.path]
864 extensions: ['.coffee', '.json']
865 cache: {}
866 packageCache: {}
867 debug: opts.debug
868 # incremental options
869 fullPaths: true
870 cacheFile: browserifyCacheFile
871 }
872
873 self.events.emit 'browserify:options', browserifyOptions
874
875 bundler = browserify(browserifyOptions)
876
877 bundler.$meta = {}
878
879 bundler = filters.watchify.setup(bundler)
880 browserifyInc(bundler)
881
882 if filters.watchify.watch is true
883 watchify = loader 'watchify'
884 bundler = watchify(bundler, ignoreWatch: true, poll: true)
885 watchStreams.push bundler
886
887 dirs = do ->
888 for d in baseDirsRealpaths
889 d + Path.sep + name
890 overridePath = removeDirs file.path, dirs
891
892 bundler.on 'update', =>
893 colors = loader 'colors'
894 process.nextTick =>
895 dest = Path.resolve(@config.dest)
896 console.log colors.cyan('Bundling'), removeDirs(file.path, [dest])
897
898 watchifyOverrides[name] = [overridePath]
899 buildTasks["build:#{name}:scripts"]().then ->
900 delete watchifyOverrides[name]
901 return
902
903 bundler
904 .on 'bundle', (output) =>
905 output.on 'end', =>
906 if filters.watchify.watch is true
907 @events.emit('browserify:bundle-end')
908 livereloadWildcard()
909 return
910
911 @bundles[file.path] = bundler
912
913 file.contents = bundler.bundle().on 'error', handleError
914
915 if filters.watchify.watch and not bundler.$meta.incremental
916 bundler.$meta.incremental = true
917 for path, value of bundler._options.cache
918 bundler.emit('file', path)
919 callback null, file
920
921
922 isBundle = (file) -> file.isBundle is true
923
924 squashBundleNames = ->
925 fs = loader 'fs'
926 gulpif = loader 'gulpif'
927 through = loader 'through'
928 streamify = loader 'streamify'
929
930 gulpif checkSourceMaps, passNext(),
931 gulpif isBundle,
932 streamify through.obj (file, enc, callback) ->
933 contents = file.contents.toString(enc)
934
935 do ->
936 regex = /"(\/[\w\W]+?)"/g
937 res = contents.match regex
938
939 i = 0
940 references = {}
941 if res and res.length > 0
942 for string in res when string.length > 2
943 if fs.existsSync string.substring(1, string.length - 1)
944 if references[string]
945 contents = contents.replace string, references[string]
946 else
947 i++
948 contents = contents.replace string, i
949 references[string] = i
950
951 file.contents = new Buffer(contents)
952 callback null, file
953
954 transformToES6 = () ->
955 through = loader 'through'
956 streamify = loader 'streamify'
957
958 streamify through.obj (file, enc, callback) ->
959 if file.isNull()
960 callback null, file
961 return
962 else if file.isStream()
963 return throw new Error('Streams are not supported!')
964
965 if isBundle(file)
966 return callback null, file
967
968 if isBowerComponent(file.path)
969 return callback null, file
970
971 if file.coffee
972 return callback null, file
973
974 contents = file.contentsToString ?= file.contents.toString enc
975 options = lo.extend {}, filters.babelify()
976
977 if file.sourceMap
978 options.inputSourceMap = file.sourceMap
979
980 options.filename = file.path
981
982 try
983 babel = loader 'babel'
984 result = babel.transform(contents, options)
985 catch error
986 @emit('error', error)
987 return callback()
988
989 transformed = result.code
990 file.contents = new Buffer transformed, enc
991
992 if file.sourceMap
993 applySourceMap = loader 'applySourceMap'
994 applySourceMap file, result.map
995
996 callback null, file
997
998 markAsCoffeeScript = ->
999 through = loader 'through'
1000 through.obj (file, enc, callback) ->
1001 file.coffee = true
1002 callback null, file
1003
1004 simpleUglify = {
1005 mangle: false
1006 compress:
1007 angular: true,
1008 sequences: false, # join consecutive statemets with the “comma operator”
1009 properties: false, # optimize property access: a["foo"] → a.foo
1010 dead_code: false, # discard unreachable code
1011 drop_debugger: false, # discard “debugger” statements
1012 unsafe: false, # some unsafe optimizations (see below)
1013 conditionals: false, # optimize if-s and conditional expressions
1014 comparisons: false, # optimize comparisons
1015 evaluate: true, # evaluate constant expressions
1016 booleans: false, # optimize boolean expressions
1017 loops: false, # optimize loops
1018 unused: false, # drop unused variables/functions
1019 hoist_funs: true, # hoist function declarations
1020 hoist_vars: false, # hoist variable declarations
1021 if_return: false, # optimize if-s followed by return/continue
1022 join_vars: true, # join var declarations
1023 cascade: false, # try to cascade `right` into `left` in sequences
1024 side_effects: false, # drop side-effect-free statements
1025 warnings: false, # warn about potentially dangerous optimizations/code
1026 }
1027
1028 bundleUglfy = lo.extend {}, simpleUglify, mangle: true
1029
1030 pipes.scripts = (pipe, name) =>
1031 collectionDefition = @collections[name]
1032
1033 doBrowserify = (file) ->
1034 if isBowerComponent file.path
1035 return false
1036 if collectionDefition.browserify?
1037 return collectionDefition.browserify is true
1038 return false
1039
1040 plumber = loader 'plumber'
1041 streamify = loader 'streamify'
1042 gulpif = loader 'gulpif'
1043 sourcemaps = loader 'sourcemaps'
1044 coffee = loader 'coffee'
1045 ngAnnotate = loader 'ngAnnotate'
1046 uglify = loader 'uglify'
1047 rename = loader 'rename'
1048 concat = loader 'concat'
1049
1050 if isProduction()
1051 return pipe
1052 .pipe plumber(errorHandler: handleError)
1053 .pipe only '.coffee', '.js'
1054
1055 .pipe gulpif doBrowserify, browserifyBundle(name)
1056 .pipe gulpif isProduction, squashBundleNames()
1057 .pipe gulpif isBundle, passNext(), transformCache.fill(name)
1058
1059 .pipe streamify sourcemaps.init(loadMaps: true)
1060
1061 .pipe gulpif transformCache.notProcessed, streamify(swapWithMinFile())
1062 .pipe gulpif isCoffee, markAsCoffeeScript()
1063
1064 .pipe gulpif transformCache.notProcessed, gulpif(isBundle, streamify(passNext()), gulpif(isCoffee, coffee(@config.coffee)))
1065
1066 .pipe gulpif transformCache.notProcessed, gulpif(isCoffee, passNext(), transformToES6(name))
1067
1068 .pipe rename extname: '.js'
1069
1070 .pipe gulpif transformCache.notProcessed, streamify ngAnnotate(filters.ngAnnotate)
1071
1072 .pipe gulpif transformCache.notProcessed,
1073 gulpif isBundle,
1074 streamify(uglify(bundleUglfy)),
1075 # else
1076 streamify(uglify(filters.uglify))
1077
1078 .pipe gulpif isBundle, streamify(passNext()), transformCache.add(name)
1079
1080 .pipe streamify(concat(name+'.js', {newLine: "\n"}))
1081
1082 .pipe streamify rename(dirname: @config.dest)
1083 .pipe streamify globCheckSkipClean(name, 'js')
1084 .pipe streamify rename(dirname: '.')
1085
1086 .pipe streamify hashify()
1087 .pipe streamify unrelativize()
1088
1089 .pipe streamify plumber.stop()
1090
1091 .pipe streamify gulpif(checkSourceMaps, sourcemaps.write('.'))
1092
1093 .pipe squashBundleNames()
1094
1095 .pipe gulp.dest @config.dest + '/'
1096
1097 .pipe streamify @finalizeManifest name, 'js'
1098 .pipe streamify gulpif(isBundle, passNext(), livereloadPipe())
1099
1100 pipe
1101 .pipe plumber(errorHandler: handleError)
1102 .pipe only '.coffee', '.js'
1103
1104 .pipe gulpif doBrowserify, browserifyBundle(name)
1105 .pipe gulpif isBundle, passNext(), globCheckSkipClean(name, 'js')
1106
1107 .pipe streamify sourcemaps.init(loadMaps: true)
1108
1109 .pipe streamify gulpif isCoffee, passNext(), transformToES6 name
1110 .pipe streamify gulpif isBundle, passNext(), gulpif isCoffee, coffee(@config.coffee)
1111
1112 .pipe rename extname: '.js'
1113 .pipe streamify hashify()
1114 .pipe streamify normalizeDotPaths name
1115 .pipe streamify unrelativize()
1116 .pipe plumber.stop()
1117
1118 .pipe streamify sourcemaps.write(includeContent:true, sourceRoot: '/src')
1119
1120 .pipe streamify gulp.dest(@config.dest + "/#{name}")
1121 .pipe streamify @finalizeManifest name, 'js'
1122 .pipe streamify gulpif isBundle, passNext(), livereloadPipe()
1123
1124 pipes.styles = (pipe, name) =>
1125 plumber = loader 'plumber'
1126 gulpif = loader 'gulpif'
1127 sass = loader 'sass'
1128 less = loader 'less'
1129 autoprefixer = loader 'autoprefixer'
1130 cssmin = loader 'cssmin'
1131 streamify = loader 'streamify'
1132 concat = loader 'concat'
1133 rename = loader 'rename'
1134
1135 pipe
1136 .pipe plumber(errorHandler: handleError)
1137 .pipe only '.less', '.css', '.scss'
1138 .pipe gulpif isProduction, passNext(), globCheckSkipClean(name, 'css')
1139
1140 .pipe gulpif isProduction, transformCache.fill(name)
1141
1142 .pipe gulpif isProduction, gulpif transformCache.notProcessed, swapWithMinFile()
1143
1144 .pipe gulpif transformCache.notProcessed, gulpif isSass, sass(@config.sass)
1145 .pipe gulpif transformCache.notProcessed, gulpif isLess, less(@config.less)
1146 .pipe gulpif transformCache.notProcessed, autoprefixer filters.autoprefixer
1147
1148 .pipe gulpif isProduction, gulpif transformCache.notProcessed, cssmin(filters.cssmin)
1149
1150 .pipe gulpif isProduction, transformCache.add(name)
1151 .pipe gulpif isProduction, concat(name+'.css')
1152
1153 .pipe streamify gulpif(isProduction, rename(dirname: @config.dest))
1154 .pipe streamify gulpif(isProduction, globCheckSkipClean(name, 'css'))
1155 .pipe streamify gulpif(isProduction, rename(dirname: '.'))
1156
1157 .pipe streamify hashify()
1158 .pipe streamify normalizeDotPaths name
1159 .pipe streamify unrelativize()
1160 .pipe plumber.stop()
1161
1162 .pipe streamify gulpif(isProduction, gulp.dest(@config.dest + '/'), gulp.dest(@config.dest + "/#{name}"))
1163
1164 .pipe @finalizeManifest name, 'css'
1165 .pipe streamify livereloadPipe
1166
1167 bulkRunning = false
1168
1169 bulkWatching = false
1170
1171 overrides = {}
1172
1173 watchifyOverrides = {}
1174
1175 watchesTasks = {}
1176
1177 buildTasks = {}
1178
1179 startedWatches = []
1180
1181 discoverFolderByFeature = (name, definition) ->
1182 Promise = loader 'Promise'
1183 fs = loader 'fs'
1184 return new Promise (resolvePromise) ->
1185 excludes = []
1186
1187 walkDirTree = (dir) ->
1188 results = []
1189 entries = fs.readdirSync(dir)
1190 hasAsset = false
1191 entries.forEach (file) ->
1192 if isScript(file) or isStyle(file)
1193 hasAsset = true
1194 return false
1195
1196 if not hasAsset
1197 excludes.push dir
1198 else
1199 entries.forEach (file) ->
1200 if file is 'bower_components'
1201 return
1202
1203 file = dir + '/' + file
1204 stat = fs.statSync(file)
1205 if stat and stat.isDirectory()
1206 results.push file
1207 results = results.concat(walkDirTree(file))
1208 return
1209
1210 return results
1211
1212 glob = loader 'glob'
1213 glob baseDirsGlob + "/#{name}", (err, directories) ->
1214 [directory] = directories
1215
1216 paths = walkDirTree directory
1217 paths = do ->
1218 p for p in paths when excludes.indexOf(p) is -1
1219
1220 definition.js ?= []
1221 definition.css ?= []
1222
1223 rest = '[^_]*.{js,coffee}'
1224 main = '{index,' + name + '}.{js,coffee}'
1225
1226 if definition.browserify isnt on
1227 definition.js.push rest
1228 definition.js.push main
1229
1230 rest = '[^_]*.{css,less,scss}'
1231 main = '{index,' + name + '}.{css,less,scss}'
1232 definition.css.push rest
1233 definition.css.push main
1234
1235 for path in paths
1236 basename = Path.basename path
1237 corrected = path.substring directory.length + 1
1238
1239 rest = corrected + '/[^_]*.{js,coffee}'
1240 main = corrected + '/{' + basename + ',index}.{js,coffee}'
1241
1242 if definition.browserify isnt on
1243 definition.js.push rest
1244 definition.js.push main
1245
1246 rest = corrected + '/[^_]*.{css,less,scss}'
1247 main = corrected + '/{' + basename + ',index}.{css,less,scss}'
1248 definition.css.push rest
1249 definition.css.push main
1250
1251 resolvePromise()
1252
1253 createTasks = (name, definition) =>
1254 taskName = "build:#{name}"
1255
1256 definition.js ?= []
1257 definition.css ?= []
1258
1259 unless Array.isArray(definition.js)
1260 throw "Collection #{name} js definition is not an array"
1261
1262 unless Array.isArray(definition.css)
1263 throw "Collection #{name} css definition is not an array"
1264
1265 if definition.browserify is undefined
1266 unless definition.discover
1267 definition.browserify = true
1268
1269 if definition.js.length is 0
1270 definition.js = ["{index,main,#{name}}.{js,coffee}"]
1271
1272 if definition.browserify and definition.js.length > 1
1273 yep = do ->
1274 for f in definition.js
1275 continue if isRawFile f
1276 f
1277 if yep.length > 1
1278 colors = loader 'colors'
1279 console.log colors.yellow('Warning'), "Browserify collections should have only one entry file (#{name} collection)"
1280
1281 processScripts = =>
1282 Promise = loader 'Promise'
1283 return new Promise (resolve, reject) =>
1284 log taskName + ':scripts'
1285
1286 if filters.watchify.watch and overrides[name]?.js and not definition.browserify
1287 files = overrides[name].js
1288 else if filters.watchify.watch and watchifyOverrides[name]
1289 files = watchifyOverrides[name]
1290 else
1291 files = excludeRawFiles @collections[name].js or []
1292
1293 files = prefixWithBase name, files
1294
1295 pipe = gulp.src files
1296 stream = pipes.scripts pipe, name
1297 stream.on 'finish', resolve
1298 stream.on 'error', reject
1299 stream
1300
1301 buildTasks["build:#{name}:scripts"] = processScripts
1302
1303 processFonts = =>
1304 Promise = loader 'Promise'
1305 if definition.fonts
1306 newer = loader 'newer'
1307 rename = loader 'rename'
1308
1309 return new Promise (resolve, reject) =>
1310 log taskName + ':fonts'
1311
1312 fonts = prefixWithBase name, definition.fonts
1313 stream = gulp.src fonts
1314 .pipe rename dirname: ''
1315 .pipe newer @config.dest + "/#{name}/fonts"
1316 .pipe gulp.dest @config.dest + "/#{name}/fonts"
1317
1318 stream.on 'finish', resolve
1319 stream.on 'error', reject
1320 else
1321 return Promise.resolve true
1322
1323 processCopy = =>
1324 Promise = loader 'Promise'
1325 return new Promise (done) =>
1326 return done(true) unless definition.copy
1327
1328 scanBasedirs().then =>
1329 log taskName + ':copy'
1330
1331 copyTasks = []
1332 promises = []
1333
1334 for from, to of definition.copy then do (from, to) =>
1335 t = "#{taskName}:copy:" + from
1336 copyTasks.push t
1337
1338 finalLocation = @config.dest + "/#{name}/#{to}"
1339 fileGlob = "#{baseDirsGlob}/#{name}/#{from}"
1340
1341 dirs = lo.extend [], baseDirsRealpaths
1342 dirs.push d + "/#{name}/#{from}" for d, i in dirs
1343
1344 through = loader 'through'
1345 renameDirectoryItems = through.obj (file, enc, callback) ->
1346 if file.isNull()
1347 callback null, file
1348 return
1349 else if file.isStream()
1350 return throw new Error('Streams are not supported!')
1351
1352 file.originalPath = file.path + ''
1353 path = removeDirs file.path, dirs
1354 file.path = path
1355
1356 callback null, file
1357 return
1358
1359 promises.push new Promise (resolve) ->
1360 glob = loader 'glob'
1361 fs = loader 'fs'
1362 rename = loader 'rename'
1363 newer = loader 'newer'
1364
1365 glob fileGlob, (err, files) ->
1366 files.forEach (v) ->
1367 fs.stat v, (err, stat) ->
1368 if err
1369 return handleError err
1370
1371 if stat.isFile()
1372 gulp.task t, ->
1373 gulp.src fileGlob
1374 .pipe rename dirname: ''
1375 .pipe newer finalLocation
1376 .pipe gulp.dest finalLocation
1377 resolve()
1378
1379 if stat.isDirectory()
1380 gulp.task t, ->
1381 gulp.src fileGlob + '/**/*'
1382 .pipe renameDirectoryItems
1383 .pipe newer finalLocation
1384 .pipe gulp.dest finalLocation
1385 resolve()
1386
1387 Promise.all(promises).then ->
1388 runSequence copyTasks...
1389 done()
1390
1391 processRaw = =>
1392 Promise = loader 'Promise'
1393 return new Promise (done) =>
1394 for ext in ['css', 'js'] then do (ext) =>
1395 files = @collections[name][ext]
1396 env = if isProduction() then 'production' else 'local'
1397
1398 addRaw = (name, key, value) =>
1399 @manifest[name] ?= {}
1400 @manifest[name][env] ?= {}
1401 @manifest[name][env][ext] ?= {}
1402 @manifest[name][env][ext][key] = value
1403
1404 for file, i in files when isRawFile file
1405 addRaw(name, file, file)
1406 done()
1407
1408 processStyles = =>
1409 Promise = loader 'Promise'
1410 return new Promise (resolve, reject) =>
1411 log taskName + ':styles'
1412
1413 if filters.watchify.watch and overrides[name]?.css
1414 files = overrides[name].css
1415 else
1416 files = excludeRawFiles @collections[name].css or []
1417
1418 files = prefixWithBase name, files
1419
1420 pipe = gulp.src files
1421 stream = pipes.styles pipe, name
1422 stream.on 'finish', resolve
1423 stream.on 'error', reject
1424 stream
1425
1426 processBower = =>
1427 Promise = loader 'Promise'
1428 return new Promise (done) =>
1429 log taskName + ':bower'
1430
1431 options = bowerJson: definition.bower or 'bower.json'
1432 bowerGlob = baseDirsGlob + '/' + name + '/' + options.bowerJson
1433
1434 glob = loader 'glob'
1435 wiredep = loader 'wiredep'
1436 glob bowerGlob, {}, (err, files) =>
1437 delete options.bowerJson
1438 options.cwd = Path.dirname Path.resolve files[0]
1439 try
1440 results = wiredep options
1441 catch error
1442 error.message += " in collection #{name} - #{definition.bower}"
1443 handleError error
1444 return done()
1445
1446 removeDirsMap = (v) -> removeDirs v, collectionDirs
1447
1448 for ext in ['css', 'js']
1449 found = results[ext]
1450
1451 if found
1452 collectionDirs = []
1453 collectionDirs.push("#{d}/#{name}") for d in baseDirsRealpaths
1454 found = found.map removeDirsMap
1455 combined = []
1456
1457 # Bower dependencies after raw files
1458 addedFound = false
1459
1460 if @collections[name][ext]? and @collections[name][ext].length > 0
1461 for f in @collections[name][ext]
1462 if not isRawFile(f) and not addedFound
1463 combined = combined.concat found
1464 addedFound = true
1465 combined.push f
1466 else
1467 combined = found
1468
1469 @collections[name][ext] = combined
1470
1471 done()
1472
1473 minimatchRegexes = {}
1474
1475 reorderManifest = =>
1476 Promise = loader 'Promise'
1477 return new Promise (done) =>
1478 log "reorder-manifest-#{name}"
1479
1480 env = if isProduction() then 'production' else 'local'
1481
1482 for ext in ['css', 'js'] then do (ext) =>
1483 files = @collections[name][ext]
1484 built = @manifest[name][env][ext]
1485
1486 ordered = {}
1487
1488 for file in files
1489 if isRawFile(file)
1490 ordered[file] = file
1491 else
1492 for k, v of built
1493 relativeFilename = removeDirs k, baseDirsRealpaths
1494 relativeFilename = fixSlashes relativeFilename
1495
1496 if file.indexOf(baseDirsGlob + '/') is 0
1497 match = name + '/' + file
1498 else
1499 match = name + '/' + file
1500
1501 minimatch = loader 'minimatch'
1502 regex = minimatchRegexes[match] ?= minimatch.makeRe match
1503
1504 if relativeFilename.match(regex)
1505 v = removeDirs v, baseDirsRealpaths
1506 v = fixSlashes v
1507 ordered[relativeFilename] = v
1508
1509 if isProduction()
1510 mainFile = name + '.' + ext
1511 @manifest[name][env][ext] ?= {}
1512 builtPath = @manifest[name][env][ext][mainFile]
1513
1514 if builtPath
1515 relativeFilename = removeDirs builtPath, baseDirsRealpaths
1516 relativeFilename = fixSlashes relativeFilename
1517 ordered[mainFile] = relativeFilename
1518
1519 @manifest[name][env][ext] = ordered
1520 done()
1521
1522 watchesTasks["watch:#{name}:start"] = (done) =>
1523 startedWatches.push name
1524
1525 files = []
1526 files = files.concat excludeRawFiles @collections[name].js or []
1527 files = files.concat excludeRawFiles @collections[name].css or []
1528 files = prefixWithBase name, files
1529
1530 if definition.bower?
1531 defaults = bowerJson: 'bower.json'
1532 options = lo.extend(defaults, definition.bower)
1533 bowerGlob = baseDirsGlob + "/#{name}/" + options.bowerJson
1534 stream = gulp.watch bowerGlob, ['build']
1535 watchStreams.push stream
1536
1537 livereload = loader 'livereload'
1538
1539 filters.watchify.watch = true
1540 livereload.listen(filters.livereload)
1541
1542 do (name) =>
1543 watches = prefixWithBase name, excludeRawFiles @collections[name].watch or []
1544 delete overrides[name]
1545
1546 s = gulp.watch(watches).on 'change', ->
1547 runSequence ['force-build'], ['build:' + name]
1548
1549 watchStreams.push s
1550
1551
1552 s2 = gulp.watch(files).on 'change', (event) =>
1553 return if force
1554
1555 overrides[name] ?= {css: [], js: []}
1556 overrides[name].css = ['__']
1557 overrides[name].js = ['__']
1558
1559 eventPath = event.path
1560
1561 dirs = do ->
1562 for d in baseDirsRealpaths
1563 d + Path.sep + name
1564
1565 overridePath = removeDirs eventPath, dirs
1566
1567 if isScript(eventPath)
1568 overrides[name].js = [overridePath]
1569 else
1570 overrides[name].css = [overridePath]
1571
1572 if not @bundles[eventPath]
1573 fin = ->
1574 ignore = {
1575 bower: true
1576 copy: true
1577 fonts: true
1578 }
1579
1580 if isScript(eventPath)
1581 ignore.css = true
1582 buildCollection fin, ignore
1583 else if isStyle(eventPath)
1584 ignore.js = true
1585 buildCollection fin, ignore
1586 else
1587 gulp.start 'build:' + name
1588
1589 watchStreams.push s2
1590
1591 done()
1592
1593 gulp.task "watch:#{name}", (done) ->
1594 tasks = []
1595 if not bulkWatching
1596 tasks.push ["build:#{name}"]
1597
1598 scanBasedirs()
1599 .then ->
1600 if definition.discover
1601 discoverFolderByFeature(name, definition)
1602 else
1603 Promise = loader 'Promise'
1604 Promise.resolve true
1605 .then ->
1606 fn1 = ->
1607 watchesTasks["watch:#{name}:start"]( -> )
1608
1609 if not buildsCount and not bulkRunning
1610 buildsCount++
1611
1612 if not bulkRunning
1613 watchGit()
1614
1615 done()
1616
1617 if not bulkWatching
1618 runSequence ["build:#{name}"], fn1
1619 else
1620 fn1()
1621
1622 .catch (err) ->
1623 console.error err
1624 return
1625
1626 buildCollection = (done, ignore = {}) ->
1627 initTransformCache()
1628
1629 Promise = loader 'Promise'
1630
1631 scanBasedirs().then ->
1632 if definition.bower and not ignore.bower
1633 processBower()
1634 else
1635 Promise.resolve true
1636 .then ->
1637 if definition.discover
1638 discoverFolderByFeature(name, definition)
1639 else
1640 Promise.resolve true
1641 .then ->
1642 mainTasks = []
1643
1644 if not ignore.css
1645 mainTasks.push processStyles()
1646
1647 mainTasks.push processRaw()
1648
1649 if definition.js and not ignore.js
1650 mainTasks.push processScripts()
1651
1652 if definition.fonts and not ignore.fonts
1653 mainTasks.push processFonts()
1654
1655 if definition.copy and not ignore.copy
1656 mainTasks.push processCopy()
1657
1658 return Promise.all(mainTasks)
1659 .then ->
1660 reorderManifest()
1661 .catch (err) ->
1662 handleError err
1663 .then ->
1664 if bulkRunning
1665 done()
1666 else
1667 saveManifest().then ->
1668 done()
1669 return
1670
1671 gulp.task taskName, buildCollection
1672
1673 createTasks(name, definition) for name, definition of @collections
1674
1675 saveManifest = =>
1676 Promise = loader 'Promise'
1677 return new Promise (resolve) =>
1678 log 'basset:save-manifest'
1679 force = false
1680 @writeManifest()
1681 @events.emit('save:manifest', @config.manifest)
1682 transformCache.save()
1683 resolve()
1684
1685 closeAllWatches = (closeLivereloadServer = false) ->
1686 for s in watchStreams
1687 if typeof s.close is 'function'
1688 s.close()
1689 else
1690 s.end()
1691
1692 watchStreams = []
1693
1694 if closeLivereloadServer
1695 livereload = loader 'livereload'
1696 livereload.server.close()
1697
1698 watchGit = =>
1699 return if watchGit.metaRunning
1700
1701 watchGit.metaRunning = true
1702
1703 s = gulp.watch([
1704 './.git/HEAD'
1705 './.git/refs/heads/**/*'
1706 ]).on 'change', =>
1707 console.log 'Detected Git change'
1708
1709 overrides = {}
1710
1711 closeAllWatches()
1712
1713 watchGit.metaRunning = false
1714
1715 blockLivereload = true
1716
1717 restartTasks = []
1718
1719 if bulkWatching
1720 restartTasks.push ['basset:watch']
1721 else
1722 for w in startedWatches
1723 restartTasks.push "watch:#{w}"
1724
1725 del = loader 'del'
1726 del([@config.dest + '/' + @config.manifest], force: true).then =>
1727 log baseDirsRealpaths
1728
1729 loadedDefinitions = lo.cloneDeep @loadedDefinitions
1730 @reset()
1731
1732 for f in loadedDefinitions
1733 @loadFile f
1734 colors = loader 'colors'
1735 console.log colors.cyan('Loading'), f
1736
1737 blockLivereload = false
1738 runSequence restartTasks...
1739 .catch handleError
1740
1741 watchStreams.push s
1742
1743 gulp.task 'basset:watch', (done) =>
1744 bulkWatching = true
1745 ts = Object.keys(@collections).map (v) -> "watch:#{v}"
1746 ts.unshift 'build'
1747
1748 watchGit()
1749
1750 scanBasedirs().then ->
1751 runSequence ts, ->
1752 done()
1753 return
1754 return
1755
1756 gulp.task 'force-build', -> force = true
1757
1758 gulp.task 'clean', =>
1759 paths = [
1760 @config.dest + '/*' + if isProduction() then '' else '*/*.*'
1761 '!' + @config.dest + '/.gitignore'
1762 ]
1763
1764 if argv.cache
1765 paths = [
1766 CACHES_PATH + Path.sep + '*.tmp'
1767 ]
1768 else
1769 paths.push '!' + CACHES_PATH + Path.sep + '*.tmp'
1770
1771 log 'Deleting', paths
1772 through = loader 'through'
1773 gulp.src(paths, read: false)
1774 .pipe through.obj (file, enc, callback) ->
1775 del = loader 'del'
1776 del(file.path, {force:true}).then ->
1777 callback()
1778
1779 gulp.task 'build', (done) =>
1780 bulkRunning = true
1781 tasks = Object.keys(@collections).map (v) -> "build:#{v}"
1782
1783 scanBasedirs().then =>
1784 runSequence tasks, ->
1785 bulkRunning = false
1786
1787 if not buildsCount
1788 buildsCount++
1789
1790 saveManifest().then ->
1791 done()
1792 return
1793 return
1794
1795 gulp.task 'b', ['build']
1796 gulp.task 'w', ['basset:watch']
1797 gulp.task 'c', ['clean']
1798
1799 gulp.task 'production', ->
1800 process.env.NODE_ENV = 'production'
1801
1802 gulp.task 'local', ->
1803 process.env.NODE_ENV = 'local'
1804
1805module.exports = new Basset
1806module.exports.Basset = Basset