]> git.immae.eu Git - github/fretlink/purs-loader.git/blobdiff - src/index.js
Escape require paths for windows
[github/fretlink/purs-loader.git] / src / index.js
index 23a99dd856dc908b56040e8ece6540054997e8d9..cd85c89a9a57d9364547b485e1e0e39b93fbb1b2 100644 (file)
@@ -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) {
@@ -77,7 +79,7 @@ module.exports = function purescriptLoader(source, map) {
     })
   }
 
-  const psModuleName = match(psModuleRegex, source)
+  const psModuleName = match(srcModuleRegex, source)
   const psModule = {
     name: psModuleName,
     load: js => callback(null, js),
@@ -135,27 +137,25 @@ function toJavaScript(psModule) {
 
   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
@@ -217,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)
@@ -255,8 +271,6 @@ function rebuild(psModule) {
       })
     })
 
-    ideClient.stderr.once('data', data => reject(data.toString()))
-
     ideClient.stdin.write(JSON.stringify(body))
     ideClient.stdin.write('\n')
   })
@@ -357,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
@@ -467,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'))
   }, [])
 }