X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=src%2Findex.js;h=bd2a35d05435268319001d8795c926acba4d697c;hb=58f78e37f04f5f64d2d6f40acf650379b9f7eab2;hp=0a25ccdbced715eaa79c03407969bddb50de84cf;hpb=7de41f10b4ff0f0d6b45d59bee0f166c3cfe3f9f;p=github%2Ffretlink%2Fpurs-loader.git diff --git a/src/index.js b/src/index.js index 0a25ccd..bd2a35d 100644 --- a/src/index.js +++ b/src/index.js @@ -10,7 +10,8 @@ const spawn = require('child_process').spawn const path = require('path') const retryPromise = require('promise-retry') -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) { @@ -25,6 +26,7 @@ module.exports = function purescriptLoader(source, map) { pscArgs: {}, pscBundle: 'psc-bundle', pscBundleArgs: {}, + pscIde: false, pscIdeColors: webpackOptions.psc === 'psa' || query.psc === 'psa', pscIdeArgs: {}, bundleOutput: 'output/bundle.js', @@ -56,7 +58,7 @@ module.exports = function purescriptLoader(source, map) { // invalidate loader cache when bundle is marked as invalid (in watch mode) this._compiler.plugin('invalid', () => { cache = config.purescriptLoaderCache = { - rebuild: true, + rebuild: options.pscIde, deferred: [], ideServer: cache.ideServer } @@ -64,19 +66,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), @@ -88,6 +90,8 @@ module.exports = function purescriptLoader(source, map) { cache: cache, } + debug('loader called', psModule.name) + if (options.bundle) { cache.bundleModules.push(psModule.name) } @@ -100,7 +104,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) } @@ -108,7 +112,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() @@ -128,11 +132,11 @@ 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 = '' @@ -144,15 +148,13 @@ function toJavaScript(psModule) { } 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("' + result.psModuleMap[p1].src + '")' + }) + .replace(/require\(['"]\.\/foreign['"]\)/g, (m, p1) => { + return 'require("' + result.psModuleMap[psModule.name].ffi + '")' }) - .replace(/require\(['"]\.\/foreign['"]\)/g, foreignRequire) } return js @@ -164,12 +166,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, @@ -184,15 +183,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) } }) @@ -215,14 +216,36 @@ function rebuild(psModule) { const args = dargs(options.pscIdeArgs) const ideClient = spawn('psc-ide-client', args) - ideClient.stdout.once('data', data => { - const res = JSON.parse(data.toString()) - debug(res) + var stdout = '' + var stderr = '' + + ideClient.stdout.on('data', data => { + stdout = stdout + data.toString() + }) + + ideClient.stderr.on('data', data => { + stderr = stderr + data.toString() + }) - if (!Array.isArray(res.result)) { + 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(stdout.toString()) + debug(res) + } catch (err) { + return reject(err) + } + + if (res && !Array.isArray(res.result)) { return res.resultType === 'success' ? resolve(psModule) - : reject(res.result) + : reject('psc-ide rebuild failed') } Promise.map(res.result, (item, i) => { @@ -231,17 +254,22 @@ function rebuild(psModule) { }) .then(compileMessages => { if (res.resultType === 'error') { - cache.errors = compileMessages - reject(res.result) + if (res.result.some(item => item.errorCode === 'UnknownModule')) { + console.log('Unknown module, attempting full recompile') + return compile(psModule) + .then(() => request({ command: 'load' })) + .then(resolve) + .catch(() => reject('psc-ide rebuild failed')) + } + 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') }) @@ -251,14 +279,6 @@ function rebuild(psModule) { params: { file: psModule.srcPath, } - }).catch(res => { - if (res.resultType === 'error') { - if (res.result.some(item => item.errorCode === 'UnknownModule')) { - console.log('Unknown module, attempting full recompile') - return compile(psModule).then(() => request({ command: 'load' })) - } - } - return Promise.resolve(psModule) }) } @@ -340,7 +360,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 @@ -350,20 +370,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 @@ -460,6 +490,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')) }, []) }