aboutsummaryrefslogblamecommitdiffhomepage
path: root/index.js
blob: 449c841ac558d0aa0e4f2b8d4087499a191fa3d6 (plain) (tree)





































                                                                         
 




                                                             






















                                                                              
            



















































                                                       
                                         


                                                       


                                 


                                  
                                              







                                                                                 
                               







                                                                          


             
 


                                                                                              
 




                                                                                              


                        
'use strict';

var os = require('os');

var cp = require('child_process');

var path = require('path')

var fs = require('fs');

var glob = require('glob');

var lodash = require('lodash');

var chalk = require('chalk');

var lu = require('loader-utils');

var cwd = process.cwd();

var MODULE_RE = /(?:^|\n)module\s+([\w\.]+)/i;

var IMPORT_RE = /^\s*import\s+(?:qualified\s+)?([\w\.]+)/i;

var BOWER_PATTERN = path.join('bower_components', 'purescript-*', 'src');

var PSC_MAKE = 'psc-make';

var OUTPUT = 'output';

var OPTIONS = {
  'no-prelude': '--no-prelude',
  'no-opts': '--no-opts',
  'no-magic-do': '--no-magic-do',
  'no-tco': '--no-tco',
  'verbose-errors': '--verbose-errors',
  'output': '--output'
};

function pattern(root) {
  var as = [ BOWER_PATTERN, root ];
  return path.join('{' + as.join(',') + '}', '**', '*.purs');
}

function mkOptions(query) {
  return lodash.foldl(lodash.keys(query), function(acc, k){
    var h = function(v){return acc.concat(query[k] && OPTIONS[k] ? [v] : []);}
    if (k === OUTPUT) return h(OPTIONS[k] + '=' + query[k]);
    else return h(OPTIONS[k]);
  }, []);
}

function mkGraph(files) {
  var graph = {};

  files.forEach(function(file){
    var source = fs.readFileSync(file, {encoding: 'utf-8'});

    var result = MODULE_RE.exec(source);

    var module = result ? result[1] : null;

    var imports =
      lodash.foldl(source.split(os.EOL), function(b, a){
        var result = IMPORT_RE.exec(a);
        if (result) b.push(result[1]);
        return b;
      }, [])
    ;

    if (module) {
      graph[module] = {
        file: file,
        imports: imports || []
      };
    }
  });

  return graph;
}

function findDeps(graph, module) {
  function go(acc, module){
    var node = graph[module];

    var imports = node && node.imports;

    if (lodash.isEmpty(imports)) return acc;
    else {
      var deps =
        lodash.map(imports, function(i){
          return go(acc.concat(imports), i);
        })
      ;
      return lodash.flatten(deps);
    }
  }

  return lodash.unique(go([], module));
}

function loader(source) {
  this.cacheable && this.cacheable();

  this.clearDependencies();

  this.addDependency(this.resourcePath);

  var callback = this.async();

  var request = lu.getRemainingRequest(this)

  var root = path.dirname(path.relative(cwd, request));

  var query = lu.parseQuery(this.query);

  var opts = mkOptions(query);

  var that = this;

  glob(pattern(root), function(e, files){
    if (e !== null) callback(e);
    else {
      var cmd = cp.spawn(PSC_MAKE, opts.concat(files));

      var graph = mkGraph(files);

      cmd.on('close', function(e){
        if (e) callback(e);
        else {
          var result = MODULE_RE.exec(source);

          var module = result ? result[1] : '';

          var dependencies = findDeps(graph, module);

          var indexPath = path.join(query[OUTPUT] || OUTPUT, module, 'index.js');

          fs.readFile(indexPath, 'utf-8', function(e, output){
            if (e) callback(e);
            else {
              dependencies.forEach(function(dep){
                var module = graph[dep];
                if (module) that.addDependency(path.resolve(module.file));
              });

              callback(null, output);
            }
          });
        }
      });

      cmd.stdout.on('data', function(stdout){
        console.log('Stdout from \'' + chalk.cyan(PSC_MAKE) + '\'\n' + chalk.magenta(stdout));
      });

      cmd.stderr.on('data', function(stderr){
        console.log('Stderr from \'' + chalk.cyan(PSC_MAKE) + '\'\n' + chalk.magenta(stderr));
      });
    }
  });
}

module.exports = loader;