all files / core/lib/ compile.js

98.11% Statements 52/53
75% Branches 3/4
100% Functions 3/3
100% Lines 48/48
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84        78× 78×   78× 78× 78× 78×     78× 78× 78×     78× 78× 78×   78× 78× 78×         78× 78× 78×   78× 76×     76× 78×   76×   76× 76× 78×       76× 76×   76× 76× 76×     78× 78× 78×   472×     80× 80×   80× 80× 80×         91×      
 
'use strict'
 
let debug = require('debug')('mako:compile')
let Promise = require('bluebird')
let Queue = require('./queue')
let utils = require('./utils')
 
module.exports = Promise.coroutine(function * (build) {
  debug('compiling %j', build.entries)
  build.timeStart('compile')
 
  let writes = new WeakSet()
  let runner = build.runner
  let hooks = runner.hooks
  let entries = build.entries.map(file => build.tree.findFile(file)).filter(Boolean)
 
  // create a fresh tree for the new build
  build.timeStart('compile:clone-tree')
  let tree = build.tree = build.tree.clone()
  build.timeStop('compile:clone-tree')
 
  // prune out files that are unreachable from the chosen entry files
  build.timeStart('compile:prune-tree')
  tree.prune(entries)
  build.timeStop('compile:prune-tree')
 
  build.timeStart('precompile')
  yield runHooks('precompile', [ build ])
  build.timeStop('precompile')
 
  // forcefully break any cycles, so topological sorting is possible
  // any plugins that introduce circular dependencies should resolve them prior to this,
  // as mako cannot know what plugin authors would intend.
  build.timeStart('compile:decycle-tree')
  tree.removeCycles()
  build.timeStop('compile:decycle-tree')
 
  if (tree.size()) {
    build.timeStart('postdependencies')
    // during prewrite, files must be processed sequentially to allow unrolling cleanly
    // TODO: find a way to split the tree into subgraphs that can be processed in parallel
    for (let file of tree.getFiles({ topological: true })) {
      yield runHooks([ 'postdependencies', file.type ], [ file, build ])
    }
    build.timeStop('postdependencies')
 
    build.timeStart('write')
    var queue = new Queue({
      available: tree.getFiles().map(file => [ file ]),
      concurrency: runner.concurrency,
      factory: Promise.coroutine(write)
    })
    yield queue.promise
    build.timeStop('write')
 
    build.timeStart('postcompile')
    yield runHooks('postcompile', [ build ])
    build.timeStop('postcompile')
  }
 
  debug('compile finished (tree size: %s)', utils.treeSize(tree))
  build.timeStop('compile')
  return build
 
  function runHooks (key, args) {
    return hooks.run(key, args).then(stats => build.addTimings(stats))
  }
 
  function * write (file) {
    Iif (writes.has(file)) return
    writes.add(file)
 
    yield runHooks([ 'prewrite', file.type ], [ file, build ])
    yield runHooks([ 'write', file.type ], [ file, build ])
    yield runHooks([ 'postwrite', file.type ], [ file, build ])
 
    // during the write hooks, new files can be added to the tree
    // (eg: adding a ".gz" version of any file, adding a ".min.js" for each ".js", etc)
    // after each iteration, search the tree for any files not already in the tree
    tree.getFiles().filter(file => !writes.has(file)).forEach(file => queue.add(file))
  }
})