'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;