From 1c12889c0adf91cf3116a9d5ff44b7466b1dfcc9 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 22 Apr 2017 10:52:54 -0400 Subject: [PATCH] Support for PureScript 0.11 Resolves #89 --- README.md | 5 +- src/Psc.js | 108 --------------------- src/bundle.js | 59 +++++++++++ src/compile.js | 58 +++++++++++ src/{PscIde.js => ide.js} | 42 ++++---- src/index.js | 48 ++++++--- src/{PsModuleMap.js => purs-module-map.js} | 26 +++-- 7 files changed, 187 insertions(+), 159 deletions(-) delete mode 100644 src/Psc.js create mode 100644 src/bundle.js create mode 100644 src/compile.js rename src/{PscIde.js => ide.js} (85%) rename src/{PsModuleMap.js => purs-module-map.js} (76%) diff --git a/README.md b/README.md index c15f03c..d82893d 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,12 @@ Install with [npm](https://npmjs.org/package/purs-loader). ``` -// For PureScript 0.9 and newer +// For PureScript 0.11 and newer npm install purs-loader --save-dev +// For PureScript 0.9 and 0.10 +npm install purs-loader@purescript-0.9 --save-dev + // For PureScript 0.8 npm install purs-loader@purescript-0.8 --save-dev ``` diff --git a/src/Psc.js b/src/Psc.js deleted file mode 100644 index ffa32b7..0000000 --- a/src/Psc.js +++ /dev/null @@ -1,108 +0,0 @@ -'use strict'; - -const path = require('path'); - -const Promise = require('bluebird') - -const fs = Promise.promisifyAll(require('fs')) - -const spawn = require('cross-spawn') - -const debug = require('debug')('purs-loader'); - -const dargs = require('./dargs'); - -function compile(psModule) { - const options = psModule.options - const cache = psModule.cache - const stderr = [] - - if (cache.compilationStarted) return Promise.resolve(psModule) - - cache.compilationStarted = true - - const args = dargs(Object.assign({ - _: options.src, - output: options.output, - }, options.pscArgs)) - - debug('spawning compiler %s %o', options.psc, args) - - return (new Promise((resolve, reject) => { - debug('compiling PureScript...') - - const compilation = spawn(options.psc, args) - - compilation.stderr.on('data', data => { - stderr.push(data.toString()); - }); - - compilation.on('close', code => { - debug('finished compiling PureScript.') - cache.compilationFinished = true - if (code !== 0) { - const errorMessage = stderr.join(''); - if (errorMessage.length) { - psModule.emitError(errorMessage); - } - if (options.watch) { - resolve(psModule); - } - else { - reject(new Error('compilation failed')) - } - } else { - const warningMessage = stderr.join(''); - if (options.warnings && warningMessage.length) { - psModule.emitWarning(warningMessage); - } - resolve(psModule) - } - }) - })) - .then(compilerOutput => { - if (options.bundle) { - return bundle(options, cache).then(() => psModule) - } - return psModule - }) -} -module.exports.compile = compile; - -function bundle(options, cache) { - if (cache.bundle) return Promise.resolve(cache.bundle) - - const stdout = [] - const stderr = cache.bundle = [] - - const args = dargs(Object.assign({ - _: [path.join(options.output, '*', '*.js')], - output: options.bundleOutput, - namespace: options.bundleNamespace, - }, options.pscBundleArgs)) - - cache.bundleModules.forEach(name => args.push('--module', name)) - - debug('spawning bundler %s %o', options.pscBundle, args.join(' ')) - - return (new Promise((resolve, reject) => { - debug('bundling PureScript...') - - const compilation = spawn(options.pscBundle, args) - - compilation.stdout.on('data', data => stdout.push(data.toString())) - compilation.stderr.on('data', data => stderr.push(data.toString())) - compilation.on('close', code => { - debug('finished bundling PureScript.') - if (code !== 0) { - const errorMessage = stderr.join(''); - if (errorMessage.length) { - psModule.emitError(errorMessage); - } - return reject(new Error('bundling failed')) - } - cache.bundle = stderr - resolve(fs.appendFileAsync(options.bundleOutput, `module.exports = ${options.bundleNamespace}`)) - }) - })) -} diff --git a/src/bundle.js b/src/bundle.js new file mode 100644 index 0000000..6627ffe --- /dev/null +++ b/src/bundle.js @@ -0,0 +1,59 @@ +'use strict'; + +const path = require('path'); + +const Promise = require('bluebird') + +const fs = Promise.promisifyAll(require('fs')) + +const spawn = require('cross-spawn') + +const debug = require('debug')('purs-loader'); + +const dargs = require('./dargs'); + +module.exports = function bundle(options, cache) { + if (cache.bundle) return Promise.resolve(cache.bundle) + + const stdout = [] + + const stderr = cache.bundle = [] + + const bundleCommand = options.pscBundle || 'purs'; + + const bundleArgs = (options.pscBundle ? [] : [ 'bundle' ]).concat(dargs(Object.assign({ + _: [path.join(options.output, '*', '*.js')], + output: options.bundleOutput, + namespace: options.bundleNamespace, + }, options.pscBundleArgs))); + + cache.bundleModules.forEach(name => bundleArgs.push('--module', name)) + + debug('spawning bundler %s %o', bundleCommand, bundleArgs); + + return (new Promise((resolve, reject) => { + debug('bundling PureScript...') + + const compilation = spawn(bundleCommand, bundleArgs) + + compilation.stdout.on('data', data => stdout.push(data.toString())) + + compilation.stderr.on('data', data => stderr.push(data.toString())) + + compilation.on('close', code => { + debug('finished bundling PureScript.') + + if (code !== 0) { + const errorMessage = stderr.join(''); + if (errorMessage.length) { + psModule.emitError(errorMessage); + } + return reject(new Error('bundling failed')) + } + + cache.bundle = stderr + + resolve(fs.appendFileAsync(options.bundleOutput, `module.exports = ${options.bundleNamespace}`)) + }) + })) +}; diff --git a/src/compile.js b/src/compile.js new file mode 100644 index 0000000..8b5d87f --- /dev/null +++ b/src/compile.js @@ -0,0 +1,58 @@ +'use strict'; + +const Promise = require('bluebird'); + +const spawn = require('cross-spawn'); + +const debug = require('debug')('purs-loader'); + +const dargs = require('./dargs'); + +module.exports = function compile(psModule) { + const options = psModule.options + + const cache = psModule.cache + + const stderr = [] + + const compileCommand = options.psc || 'purs'; + + const compileArgs = (options.psc ? [] : [ 'compile' ]).concat(dargs(Object.assign({ + _: options.src, + output: options.output, + }, options.pscArgs))) + + debug('spawning compiler %s %o', compileCommand, compileArgs) + + return new Promise((resolve, reject) => { + debug('compiling PureScript...') + + const compilation = spawn(compileCommand, compileArgs) + + compilation.stderr.on('data', data => { + stderr.push(data.toString()); + }); + + compilation.on('close', code => { + debug('finished compiling PureScript.') + if (code !== 0) { + const errorMessage = stderr.join(''); + if (errorMessage.length) { + psModule.emitError(errorMessage); + } + if (options.watch) { + resolve(psModule); + } + else { + reject(new Error('compilation failed')) + } + } else { + const warningMessage = stderr.join(''); + if (options.warnings && warningMessage.length) { + psModule.emitWarning(warningMessage); + } + resolve(psModule) + } + }) + }); +}; diff --git a/src/PscIde.js b/src/ide.js similarity index 85% rename from src/PscIde.js rename to src/ide.js index bf92b38..f839fd5 100644 --- a/src/PscIde.js +++ b/src/ide.js @@ -20,7 +20,7 @@ const Psc = require('./Psc'); const PsModuleMap = require('./PsModuleMap'); -function connect(psModule) { +module.exports.connect = function connect(psModule) { const options = psModule.options const cache = psModule.cache @@ -31,14 +31,14 @@ function connect(psModule) { const connect_ = () => new Promise((resolve, reject) => { const args = dargs(options.pscIdeArgs) - debug('attempting to connect to psc-ide-server', args) + debug('attempting to run purs ide client: %o', args) - const ideClient = spawn('psc-ide-client', args) + const ideClient = spawn('purs', ['ide', 'client'].concat(args)) ideClient.stderr.on('data', data => { debug(data.toString()) cache.ideServer = false - reject(new Error('psc-ide-client failed')) + reject(new Error('purs ide client failed')) }) ideClient.stdout.once('data', data => { debug(data.toString()) @@ -49,11 +49,11 @@ function connect(psModule) { resolve(psModule) } else { cache.ideServer = ideServer - reject(new Error('psc-ide-client failed')) + reject(new Error('purs ide client failed')) } } else { cache.ideServer = false - reject(new Error('psc-ide-client failed')) + reject(new Error('purs ide client failed')) } }) ideClient.stdin.resume() @@ -66,24 +66,24 @@ function connect(psModule) { '_': options.src }, options.pscIdeServerArgs)) - debug('attempting to start psc-ide-server', serverArgs) + debug('attempting to start purs ide server: %o', serverArgs) - const ideServer = cache.ideServer = spawn('psc-ide-server', serverArgs) + const ideServer = cache.ideServer = spawn('purs', ['ide', 'server'].concat(serverArgs)) ideServer.stdout.on('data', data => { - debug('psc-ide-server stdout: %s', data.toString()); + debug('purs ide server stdout: %s', data.toString()); }); ideServer.stderr.on('data', data => { - debug('psc-ide-server stderr: %s', data.toString()); + debug('purs ide server stderr: %s', data.toString()); }); ideServer.on('error', error => { - debug('psc-ide-server error: %o', error); + debug('purs ide server error: %o', error); }); ideServer.on('close', (code, signal) => { - debug('psc-ide-server close: %s %s', code, signal); + debug('purs ide server close: %s %s', code, signal); }); return retryPromise((retry, number) => { @@ -91,7 +91,7 @@ function connect(psModule) { if (!cache.ideServer && number === 9) { debug(error) - console.warn('Failed to connect to or start psc-ide-server. A full compilation will occur on rebuild'); + console.warn('Failed to connect to or start purs ide server. A full compilation will occur on rebuild'); return Promise.resolve(psModule) } @@ -104,18 +104,17 @@ function connect(psModule) { minTimeout: 333, maxTimeout: 333, }) -} -module.exports.connect = connect; +}; -function rebuild(psModule) { +module.exports.rebuild = function rebuild(psModule) { const options = psModule.options const cache = psModule.cache - debug('attempting rebuild with psc-ide-client %s', psModule.srcPath) + debug('attempting rebuild with purs ide client %s', psModule.srcPath) const request = (body) => new Promise((resolve, reject) => { const args = dargs(options.pscIdeArgs) - const ideClient = spawn('psc-ide-client', args) + const ideClient = spawn('purs', ['ide', 'client'].concat(args)) var stdout = '' var stderr = '' @@ -130,7 +129,7 @@ function rebuild(psModule) { ideClient.on('close', code => { if (code !== 0) { - const error = stderr === '' ? 'Failed to spawn psc-ide-client' : stderr + const error = stderr === '' ? 'Failed to spawn purs ide client' : stderr return reject(new Error(error)) } @@ -187,7 +186,7 @@ function rebuild(psModule) { }) }) - debug('psc-ide-client stdin: %o', body); + debug('purs ide client stdin: %o', body); ideClient.stdin.write(JSON.stringify(body)) ideClient.stdin.write('\n') @@ -199,8 +198,7 @@ function rebuild(psModule) { file: psModule.srcPath, } }) -} -module.exports.rebuild = rebuild; +}; function formatIdeResult(result, options, index, length) { let numAndErr = `[${index+1}/${length} ${result.errorCode}]` diff --git a/src/index.js b/src/index.js index 047927c..799f8f9 100644 --- a/src/index.js +++ b/src/index.js @@ -1,15 +1,27 @@ 'use strict' const debug = require('debug')('purs-loader') + const loaderUtils = require('loader-utils') + const Promise = require('bluebird') + const path = require('path') -const PsModuleMap = require('./PsModuleMap'); -const Psc = require('./Psc'); -const PscIde = require('./PscIde'); + +const PsModuleMap = require('./purs-module-map'); + +const compile = require('./compile'); + +const bundle = require('./bundle'); + +const ide = require('./ide'); + const toJavaScript = require('./to-javascript'); + const dargs = require('./dargs'); + const spawn = require('cross-spawn').sync + const eol = require('os').EOL module.exports = function purescriptLoader(source, map) { @@ -34,9 +46,9 @@ module.exports = function purescriptLoader(source, map) { const defaultDeps = depsPaths(options.pscPackage) const defaultOptions = { context: config.context, - psc: 'psc', + psc: null, pscArgs: {}, - pscBundle: 'psc-bundle', + pscBundle: null, pscBundleArgs: {}, pscIde: false, pscIdeColors: options.psc === 'psa', @@ -102,7 +114,7 @@ module.exports = function purescriptLoader(source, map) { }); } - const psModuleName = PsModuleMap.match(source) + const psModuleName = PsModuleMap.matchModule(source) const psModule = { name: psModuleName, load: js => callback(null, js), @@ -131,8 +143,8 @@ module.exports = function purescriptLoader(source, map) { } if (cache.rebuild) { - return PscIde.connect(psModule) - .then(PscIde.rebuild) + return ide.connect(psModule) + .then(ide.rebuild) .then(toJavaScript) .then(psModule.load) .catch(psModule.reject) @@ -147,11 +159,21 @@ module.exports = function purescriptLoader(source, map) { cache.deferred.push(psModule) if (!cache.compilationStarted) { - return Psc.compile(psModule) - .then(() => PsModuleMap.makeMap(options.src).then(map => { - debug('rebuilt module map after compile'); - cache.psModuleMap = map; - })) + cache.compilationStarted = true; + + return compile(psModule) + .then(() => { + cache.compilationFinished = true; + + const bundlePromise = options.bundle ? bundle(options, cache) : Promise.resolve(); + + return bundlePromise.then(() => + PsModuleMap.makeMap(options.src).then(map => { + debug('rebuilt module map after compile'); + cache.psModuleMap = map; + }) + ); + }) .then(() => Promise.map(cache.deferred, psModule => { if (typeof cache.ideServer === 'object') cache.ideServer.kill() return toJavaScript(psModule).then(psModule.load) diff --git a/src/PsModuleMap.js b/src/purs-module-map.js similarity index 76% rename from src/PsModuleMap.js rename to src/purs-module-map.js index 0ae687c..b906d08 100644 --- a/src/PsModuleMap.js +++ b/src/purs-module-map.js @@ -14,19 +14,17 @@ const srcModuleRegex = /(?:^|\n)module\s+([\w\.]+)/i; const importModuleRegex = /(?:^|\n)\s*import\s+([\w\.]+)/ig; -function matchModule(str) { +module.exports.matchModule = function matchModule(str) { const matches = str.match(srcModuleRegex); return matches && matches[1]; -} -module.exports.match = matchModule; +}; -function matchImports(str) { +module.exports.matchImports = function matchImports(str) { const matches = str.match(importModuleRegex); return (matches || []).map(a => a.replace(/\n?\s*import\s+/i, '')); -} -module.exports.matchImports = matchImports; +}; -function makeMapEntry(filePurs) { +module.exports.makeMapEntry = function makeMapEntry(filePurs) { const dirname = path.dirname(filePurs); const basename = path.basename(filePurs, '.purs'); @@ -41,9 +39,9 @@ function makeMapEntry(filePurs) { const sourceJs = fileMap.fileJs; - const moduleName = matchModule(sourcePurs); + const moduleName = module.exports.matchModule(sourcePurs); - const imports = matchImports(sourcePurs); + const imports = module.exports.matchImports(sourcePurs); const map = {}; @@ -61,18 +59,16 @@ function makeMapEntry(filePurs) { }); return result; -} -module.exports.makeMapEntry = makeMapEntry; +}; -function makeMap(src) { +module.exports.makeMap = function makeMap(src) { debug('loading PureScript source and FFI files from %o', src); const globs = [].concat(src); return globby(globs).then(paths => - Promise.all(paths.map(makeMapEntry)).then(result => + Promise.all(paths.map(module.exports.makeMapEntry)).then(result => result.reduce(Object.assign, {}) ) ); -} -module.exports.makeMap = makeMap; +}; -- 2.41.0