From: Alex Mingoia Date: Sat, 21 May 2016 23:53:29 +0000 (-0700) Subject: Do not check-in transpiled source. X-Git-Tag: 1.0.0-rc.0~1^2~2 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=d34bc169e84d52b28279786ff400daced5fd8b8a;p=github%2Ffretlink%2Fpurs-loader.git Do not check-in transpiled source. --- diff --git a/index.js b/index.js deleted file mode 100644 index 5f41361..0000000 --- a/index.js +++ /dev/null @@ -1,478 +0,0 @@ -'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'; - }); - }, []); -}