X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;ds=sidebyside;f=src%2Findex.js;h=cd85c89a9a57d9364547b485e1e0e39b93fbb1b2;hb=f8d292fbbe2004d209ee78d3432a0340ea6cf858;hp=9db8a6337a0afe31d0f02e32b6de48bda168e4e3;hpb=5163b5a276920e80e71051266696e0d8a3908037;p=github%2Ffretlink%2Fpurs-loader.git diff --git a/src/index.js b/src/index.js index 9db8a63..cd85c89 100644 --- a/src/index.js +++ b/src/index.js @@ -6,11 +6,13 @@ const loaderUtils = require('loader-utils') const globby = require('globby') const Promise = require('bluebird') const fs = Promise.promisifyAll(require('fs')) -const spawn = require('child_process').spawn +const spawn = require('cross-spawn') const path = require('path') const retryPromise = require('promise-retry') +const jsStringEscape = require('js-string-escape') -const psModuleRegex = /(?:^|\n)module\s+([\w\.]+)/i +const ffiModuleRegex = /\/\/\s+module\s+([\w\.]+)/i +const srcModuleRegex = /(?:^|\n)module\s+([\w\.]+)/i const requireRegex = /require\(['"]\.\.\/([\w\.]+)['"]\)/g module.exports = function purescriptLoader(source, map) { @@ -65,19 +67,19 @@ module.exports = function purescriptLoader(source, map) { // add psc warnings to webpack compilation warnings this._compiler.plugin('after-compile', (compilation, callback) => { - if (options.warnings && cache.warnings && cache.warnings.length) { - compilation.warnings.unshift(`PureScript compilation:\n${cache.warnings.join('')}`) + if (options.warnings && cache.warnings) { + compilation.warnings.unshift(`PureScript compilation:\n${cache.warnings}`) } - if (cache.errors && cache.errors.length) { - compilation.errors.unshift(`PureScript compilation:\n${cache.errors.join('\n')}`) + if (cache.errors) { + compilation.errors.unshift(`PureScript compilation:\n${cache.errors}`) } callback() }) } - const psModuleName = match(psModuleRegex, source) + const psModuleName = match(srcModuleRegex, source) const psModule = { name: psModuleName, load: js => callback(null, js), @@ -89,6 +91,8 @@ module.exports = function purescriptLoader(source, map) { cache: cache, } + debug('loader called', psModule.name) + if (options.bundle) { cache.bundleModules.push(psModule.name) } @@ -101,7 +105,7 @@ module.exports = function purescriptLoader(source, map) { .catch(psModule.reject) } - if (cache.compilation && cache.compilation.length) { + if (cache.compilationFinished) { return toJavaScript(psModule).then(psModule.load).catch(psModule.reject) } @@ -109,7 +113,7 @@ module.exports = function purescriptLoader(source, map) { // references to compiled output are valid. cache.deferred.push(psModule) - if (!cache.compilation) { + if (!cache.compilationStarted) { return compile(psModule) .then(() => Promise.map(cache.deferred, psModule => { if (typeof cache.ideServer === 'object') cache.ideServer.kill() @@ -129,31 +133,29 @@ function toJavaScript(psModule) { const bundlePath = path.resolve(options.bundleOutput) const jsPath = cache.bundle ? bundlePath : psModule.jsPath - debug('loading JavaScript for', psModule.srcPath) + debug('loading JavaScript for', psModule.name) return Promise.props({ js: fs.readFileAsync(jsPath, 'utf8'), - psModuleMap: psModuleMap(options.src, cache) + psModuleMap: psModuleMap(options, cache) }).then(result => { let js = '' if (options.bundle) { // if bundling, return a reference to the bundle js = 'module.exports = require("' - + path.relative(psModule.srcDir, options.bundleOutput) + + jsStringEscape(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. - const foreignRequire = 'require("' + path.resolve( - path.join(psModule.options.output, psModule.name, 'foreign.js') - ) + '")' - js = result.js .replace(requireRegex, (m, p1) => { - return 'require("' + result.psModuleMap[p1] + '")' + return 'require("' + jsStringEscape(result.psModuleMap[p1].src) + '")' + }) + .replace(/require\(['"]\.\/foreign['"]\)/g, (m, p1) => { + return 'require("' + jsStringEscape(result.psModuleMap[psModule.name].ffi) + '")' }) - .replace(/require\(['"]\.\/foreign['"]\)/g, foreignRequire) } return js @@ -165,12 +167,9 @@ function compile(psModule) { const cache = psModule.cache const stderr = [] - if (cache.compilation) return Promise.resolve(cache.compilation) - - cache.compilation = [] - cache.warnings = [] - cache.errors = [] + if (cache.compilationStarted) return Promise.resolve(psModule) + cache.compilationStarted = true const args = dargs(Object.assign({ _: options.src, @@ -185,15 +184,17 @@ function compile(psModule) { const compilation = spawn(options.psc, args) + compilation.stdout.on('data', data => stderr.push(data.toString())) compilation.stderr.on('data', data => stderr.push(data.toString())) compilation.on('close', code => { console.log('Finished compiling PureScript.') + cache.compilationFinished = true if (code !== 0) { - cache.compilation = cache.errors = stderr + cache.errors = stderr.join('') reject(true) } else { - cache.compilation = cache.warnings = stderr + cache.warnings = stderr.join('') resolve(psModule) } }) @@ -216,11 +217,27 @@ function rebuild(psModule) { const args = dargs(options.pscIdeArgs) const ideClient = spawn('psc-ide-client', args) - ideClient.stdout.once('data', data => { + var stdout = '' + var stderr = '' + + ideClient.stdout.on('data', data => { + stdout = stdout + data.toString() + }) + + ideClient.stderr.on('data', data => { + stderr = stderr + data.toString() + }) + + ideClient.on('close', code => { + if (code !== 0) { + const error = stderr === '' ? 'Failed to spawn psc-ide-client' : stderr + return reject(new Error(error)) + } + let res = null try { - res = JSON.parse(data.toString()) + res = JSON.parse(stdout.toString()) debug(res) } catch (err) { return reject(err) @@ -245,17 +262,15 @@ function rebuild(psModule) { .then(resolve) .catch(() => reject('psc-ide rebuild failed')) } - cache.errors = compileMessages + cache.errors = compileMessages.join('\n') reject('psc-ide rebuild failed') } else { - cache.warnings = compileMessages + cache.warnings = compileMessages.join('\n') resolve(psModule) } }) }) - ideClient.stderr.once('data', data => reject(data.toString())) - ideClient.stdin.write(JSON.stringify(body)) ideClient.stdin.write('\n') }) @@ -346,7 +361,7 @@ function bundle(options, cache) { compilation.stderr.on('data', data => stderr.push(data.toString())) compilation.on('close', code => { if (code !== 0) { - cache.errors.concat(stderr) + cache.errors = (cache.errors || '') + stderr.join('') return reject(true) } cache.bundle = stderr @@ -356,20 +371,30 @@ function bundle(options, cache) { } // map of PS module names to their source path -function psModuleMap(globs, cache) { +function psModuleMap(options, cache) { if (cache.psModuleMap) return Promise.resolve(cache.psModuleMap) + const globs = [].concat(options.src).concat(options.ffi) + return globby(globs).then(paths => { return Promise .props(paths.reduce((map, file) => { map[file] = fs.readFileAsync(file, 'utf8') return map }, {})) - .then(srcMap => { - cache.psModuleMap = Object.keys(srcMap).reduce((map, file) => { - const source = srcMap[file] - const psModuleName = match(psModuleRegex, source) - map[psModuleName] = path.resolve(file) + .then(fileMap => { + cache.psModuleMap = Object.keys(fileMap).reduce((map, file) => { + const source = fileMap[file] + const ext = path.extname(file) + const isPurs = ext.match(/purs$/i) + const moduleRegex = isPurs ? srcModuleRegex : ffiModuleRegex + const moduleName = match(moduleRegex, source) + map[moduleName] = map[moduleName] || {} + if (isPurs) { + map[moduleName].src = path.resolve(file) + } else { + map[moduleName].ffi = path.resolve(file) + } return map }, {}) return cache.psModuleMap @@ -466,6 +491,6 @@ function dargs(obj) { else if (Array.isArray(val)) val.forEach(v => args.push(arg, v)) else args.push(arg, obj[key]) - return args + return args.filter(arg => (typeof arg !== 'boolean')) }, []) }