+++ /dev/null
-'use strict';
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
-
-var colors = require('chalk');
-var debug = require('debug')('purs-loader');
-var loaderUtils = require('loader-utils');
-var globby = require('globby');
-var Promise = require('bluebird');
-var fs = Promise.promisifyAll(require('fs'));
-var spawn = require('child_process').spawn;
-var path = require('path');
-var retryPromise = require('promise-retry');
-
-var psModuleRegex = /(?:^|\n)module\s+([\w\.]+)/i;
-var requireRegex = /require\(['"]\.\.\/([\w\.]+)['"]\)/g;
-
-module.exports = function purescriptLoader(source, map) {
- var callback = this.async();
- var config = this.options;
- var query = loaderUtils.parseQuery(this.query);
- var webpackOptions = this.options.purescriptLoader || {};
-
- var options = Object.assign({
- context: config.context,
- psc: 'psc',
- pscArgs: {},
- pscBundle: 'psc-bundle',
- pscBundleArgs: {},
- pscIde: false,
- pscIdeColors: webpackOptions.psc === 'psa' || query.psc === 'psa',
- pscIdeArgs: {},
- bundleOutput: 'output/bundle.js',
- bundleNamespace: 'PS',
- bundle: false,
- warnings: true,
- output: 'output',
- src: [path.join('src', '**', '*.purs'), path.join('bower_components', 'purescript-*', 'src', '**', '*.purs')],
- ffi: [path.join('src', '**', '*.js'), path.join('bower_components', 'purescript-*', 'src', '**', '*.js')]
- }, webpackOptions, query);
-
- this.cacheable && this.cacheable();
-
- var cache = config.purescriptLoaderCache = config.purescriptLoaderCache || {
- rebuild: false,
- deferred: [],
- bundleModules: []
- };
-
- if (!config.purescriptLoaderInstalled) {
- config.purescriptLoaderInstalled = true;
-
- // invalidate loader cache when bundle is marked as invalid (in watch mode)
- this._compiler.plugin('invalid', function () {
- cache = config.purescriptLoaderCache = {
- rebuild: options.pscIde,
- deferred: [],
- ideServer: cache.ideServer
- };
- });
-
- // add psc warnings to webpack compilation warnings
- this._compiler.plugin('after-compile', function (compilation, callback) {
- if (options.warnings && cache.warnings) {
- compilation.warnings.unshift('PureScript compilation:\n' + cache.warnings);
- }
-
- if (cache.errors) {
- compilation.errors.unshift('PureScript compilation:\n' + cache.errors);
- }
-
- callback();
- });
- }
-
- var psModuleName = match(psModuleRegex, source);
- var psModule = {
- name: psModuleName,
- load: function load(js) {
- return callback(null, js);
- },
- reject: function reject(error) {
- return callback(error);
- },
- srcPath: this.resourcePath,
- srcDir: path.dirname(this.resourcePath),
- jsPath: path.resolve(path.join(options.output, psModuleName, 'index.js')),
- options: options,
- cache: cache
- };
-
- debug('loader called', psModule.name);
-
- if (options.bundle) {
- cache.bundleModules.push(psModule.name);
- }
-
- if (cache.rebuild) {
- return connectIdeServer(psModule).then(rebuild).then(toJavaScript).then(psModule.load).catch(psModule.reject);
- }
-
- if (cache.compilationFinished) {
- return toJavaScript(psModule).then(psModule.load).catch(psModule.reject);
- }
-
- // We need to wait for compilation to finish before the loaders run so that
- // references to compiled output are valid.
- cache.deferred.push(psModule);
-
- if (!cache.compilationStarted) {
- return compile(psModule).then(function () {
- return Promise.map(cache.deferred, function (psModule) {
- if (_typeof(cache.ideServer) === 'object') cache.ideServer.kill();
- return toJavaScript(psModule).then(psModule.load);
- });
- }).catch(function (error) {
- cache.deferred[0].reject(error);
- cache.deferred.slice(1).forEach(function (psModule) {
- return psModule.reject(true);
- });
- });
- }
-};
-
-// The actual loader is executed *after* purescript compilation.
-function toJavaScript(psModule) {
- var options = psModule.options;
- var cache = psModule.cache;
- var bundlePath = path.resolve(options.bundleOutput);
- var jsPath = cache.bundle ? bundlePath : psModule.jsPath;
-
- debug('loading JavaScript for', psModule.name);
-
- return Promise.props({
- js: fs.readFileAsync(jsPath, 'utf8'),
- psModuleMap: psModuleMap(options.src, cache)
- }).then(function (result) {
- var js = '';
-
- if (options.bundle) {
- // if bundling, return a reference to the bundle
- js = 'module.exports = require("' + path.relative(psModule.srcDir, options.bundleOutput) + '")["' + psModule.name + '"]';
- } else {
- // replace require paths to output files generated by psc with paths
- // to purescript sources, which are then also run through this loader.
- var foreignRequire = 'require("' + path.resolve(path.join(psModule.options.output, psModule.name, 'foreign.js')) + '")';
-
- js = result.js.replace(requireRegex, function (m, p1) {
- return 'require("' + result.psModuleMap[p1] + '")';
- }).replace(/require\(['"]\.\/foreign['"]\)/g, foreignRequire);
- }
-
- return js;
- });
-}
-
-function compile(psModule) {
- var options = psModule.options;
- var cache = psModule.cache;
- var stderr = [];
-
- if (cache.compilationStarted) return Promise.resolve(psModule);
-
- cache.compilationStarted = true;
-
- var args = dargs(Object.assign({
- _: options.src,
- ffi: options.ffi,
- output: options.output
- }, options.pscArgs));
-
- debug('spawning compiler %s %o', options.psc, args);
-
- return new Promise(function (resolve, reject) {
- console.log('\nCompiling PureScript...');
-
- var compilation = spawn(options.psc, args);
-
- compilation.stdout.on('data', function (data) {
- return stderr.push(data.toString());
- });
- compilation.stderr.on('data', function (data) {
- return stderr.push(data.toString());
- });
-
- compilation.on('close', function (code) {
- console.log('Finished compiling PureScript.');
- cache.compilationFinished = true;
- if (code !== 0) {
- cache.errors = stderr.join('');
- reject(true);
- } else {
- cache.warnings = stderr.join('');
- resolve(psModule);
- }
- });
- }).then(function (compilerOutput) {
- if (options.bundle) {
- return bundle(options, cache).then(function () {
- return psModule;
- });
- }
- return psModule;
- });
-}
-
-function rebuild(psModule) {
- var options = psModule.options;
- var cache = psModule.cache;
-
- debug('attempting rebuild with psc-ide-client %s', psModule.srcPath);
-
- var request = function request(body) {
- return new Promise(function (resolve, reject) {
- var args = dargs(options.pscIdeArgs);
- var ideClient = spawn('psc-ide-client', args);
-
- ideClient.stdout.once('data', function (data) {
- var res = null;
-
- try {
- res = JSON.parse(data.toString());
- debug(res);
- } catch (err) {
- return reject(err);
- }
-
- if (res && !Array.isArray(res.result)) {
- return res.resultType === 'success' ? resolve(psModule) : reject('psc-ide rebuild failed');
- }
-
- Promise.map(res.result, function (item, i) {
- debug(item);
- return formatIdeResult(item, options, i, res.result.length);
- }).then(function (compileMessages) {
- if (res.resultType === 'error') {
- if (res.result.some(function (item) {
- return item.errorCode === 'UnknownModule';
- })) {
- console.log('Unknown module, attempting full recompile');
- return compile(psModule).then(function () {
- return request({ command: 'load' });
- }).then(resolve).catch(function () {
- return reject('psc-ide rebuild failed');
- });
- }
- cache.errors = compileMessages.join('\n');
- reject('psc-ide rebuild failed');
- } else {
- cache.warnings = compileMessages.join('\n');
- resolve(psModule);
- }
- });
- });
-
- ideClient.stderr.once('data', function (data) {
- return reject(data.toString());
- });
-
- ideClient.stdin.write(JSON.stringify(body));
- ideClient.stdin.write('\n');
- });
- };
-
- return request({
- command: 'rebuild',
- params: {
- file: psModule.srcPath
- }
- });
-}
-
-function formatIdeResult(result, options, index, length) {
- var srcPath = path.relative(options.context, result.filename);
- var pos = result.position;
- var fileAndPos = srcPath + ':' + pos.startLine + ':' + pos.startColumn;
- var numAndErr = '[' + (index + 1) + '/' + length + ' ' + result.errorCode + ']';
- numAndErr = options.pscIdeColors ? colors.yellow(numAndErr) : numAndErr;
-
- return fs.readFileAsync(result.filename, 'utf8').then(function (source) {
- var lines = source.split('\n').slice(pos.startLine - 1, pos.endLine);
- var endsOnNewline = pos.endColumn === 1 && pos.startLine !== pos.endLine;
- var up = options.pscIdeColors ? colors.red('^') : '^';
- var down = options.pscIdeColors ? colors.red('v') : 'v';
- var trimmed = lines.slice(0);
-
- if (endsOnNewline) {
- lines.splice(lines.length - 1, 1);
- pos.endLine = pos.endLine - 1;
- pos.endColumn = lines[lines.length - 1].length || 1;
- }
-
- // strip newlines at the end
- if (endsOnNewline) {
- trimmed = lines.reverse().reduce(function (trimmed, line, i) {
- if (i === 0 && line === '') trimmed.trimming = true;
- if (!trimmed.trimming) trimmed.push(line);
- if (trimmed.trimming && line !== '') {
- trimmed.trimming = false;
- trimmed.push(line);
- }
- return trimmed;
- }, []).reverse();
- pos.endLine = pos.endLine - (lines.length - trimmed.length);
- pos.endColumn = trimmed[trimmed.length - 1].length || 1;
- }
-
- var spaces = ' '.repeat(String(pos.endLine).length);
- var snippet = trimmed.map(function (line, i) {
- return ' ' + (pos.startLine + i) + ' ' + line;
- }).join('\n');
-
- if (trimmed.length === 1) {
- snippet += '\n ' + spaces + ' ' + ' '.repeat(pos.startColumn - 1) + up.repeat(pos.endColumn - pos.startColumn + 1);
- } else {
- snippet = ' ' + spaces + ' ' + ' '.repeat(pos.startColumn - 1) + down + '\n' + snippet;
- snippet += '\n ' + spaces + ' ' + ' '.repeat(pos.endColumn - 1) + up;
- }
-
- return Promise.resolve('\n' + numAndErr + ' ' + fileAndPos + '\n\n' + snippet + '\n\n' + result.message);
- });
-}
-
-function bundle(options, cache) {
- if (cache.bundle) return Promise.resolve(cache.bundle);
-
- var stdout = [];
- var stderr = cache.bundle = [];
-
- var args = dargs(Object.assign({
- _: [path.join(options.output, '*', '*.js')],
- output: options.bundleOutput,
- namespace: options.bundleNamespace
- }, options.pscBundleArgs));
-
- cache.bundleModules.forEach(function (name) {
- return args.push('--module', name);
- });
-
- debug('spawning bundler %s %o', options.pscBundle, args.join(' '));
-
- return new Promise(function (resolve, reject) {
- console.log('Bundling PureScript...');
-
- var compilation = spawn(options.pscBundle, args);
-
- compilation.stdout.on('data', function (data) {
- return stdout.push(data.toString());
- });
- compilation.stderr.on('data', function (data) {
- return stderr.push(data.toString());
- });
- compilation.on('close', function (code) {
- if (code !== 0) {
- cache.errors = (cache.errors || '') + stderr.join('');
- return reject(true);
- }
- cache.bundle = stderr;
- resolve(fs.appendFileAsync('output/bundle.js', 'module.exports = ' + options.bundleNamespace));
- });
- });
-}
-
-// map of PS module names to their source path
-function psModuleMap(globs, cache) {
- if (cache.psModuleMap) return Promise.resolve(cache.psModuleMap);
-
- return globby(globs).then(function (paths) {
- return Promise.props(paths.reduce(function (map, file) {
- map[file] = fs.readFileAsync(file, 'utf8');
- return map;
- }, {})).then(function (srcMap) {
- cache.psModuleMap = Object.keys(srcMap).reduce(function (map, file) {
- var source = srcMap[file];
- var psModuleName = match(psModuleRegex, source);
- map[psModuleName] = path.resolve(file);
- return map;
- }, {});
- return cache.psModuleMap;
- });
- });
-}
-
-function connectIdeServer(psModule) {
- var options = psModule.options;
- var cache = psModule.cache;
-
- if (cache.ideServer) return Promise.resolve(psModule);
-
- cache.ideServer = true;
-
- var connect = function connect() {
- return new Promise(function (resolve, reject) {
- var args = dargs(options.pscIdeArgs);
-
- debug('attempting to connect to psc-ide-server', args);
-
- var ideClient = spawn('psc-ide-client', args);
-
- ideClient.stderr.on('data', function (data) {
- debug(data.toString());
- cache.ideServer = false;
- reject(true);
- });
- ideClient.stdout.once('data', function (data) {
- debug(data.toString());
- if (data.toString()[0] === '{') {
- var res = JSON.parse(data.toString());
- if (res.resultType === 'success') {
- cache.ideServer = ideServer;
- resolve(psModule);
- } else {
- cache.ideServer = ideServer;
- reject(true);
- }
- } else {
- cache.ideServer = false;
- reject(true);
- }
- });
- ideClient.stdin.resume();
- ideClient.stdin.write(JSON.stringify({ command: 'load' }));
- ideClient.stdin.write('\n');
- });
- };
-
- var args = dargs(Object.assign({
- outputDirectory: options.output
- }, options.pscIdeArgs));
-
- debug('attempting to start psc-ide-server', args);
-
- var ideServer = cache.ideServer = spawn('psc-ide-server', []);
- ideServer.stderr.on('data', function (data) {
- debug(data.toString());
- });
-
- return retryPromise(function (retry, number) {
- return connect().catch(function (error) {
- if (!cache.ideServer && number === 9) {
- debug(error);
-
- console.log('failed to connect to or start psc-ide-server, ' + 'full compilation will occur on rebuild');
-
- return Promise.resolve(psModule);
- }
-
- return retry(error);
- });
- }, {
- retries: 9,
- factor: 1,
- minTimeout: 333,
- maxTimeout: 333
- });
-}
-
-function match(regex, str) {
- var matches = str.match(regex);
- return matches && matches[1];
-}
-
-function dargs(obj) {
- return Object.keys(obj).reduce(function (args, key) {
- var arg = '--' + key.replace(/[A-Z]/g, '-$&').toLowerCase();
- var val = obj[key];
-
- if (key === '_') val.forEach(function (v) {
- return args.push(v);
- });else if (Array.isArray(val)) val.forEach(function (v) {
- return args.push(arg, v);
- });else args.push(arg, obj[key]);
-
- return args.filter(function (arg) {
- return typeof arg !== 'boolean';
- });
- }, []);
-}