]> git.immae.eu Git - github/fretlink/purs-loader.git/blobdiff - src/PursLoader/Loader.purs
Bumping version number to 0.6.0-beta.1
[github/fretlink/purs-loader.git] / src / PursLoader / Loader.purs
index b5795771134f0d2722c0607ea70c986a1c008fe6..5e00f7ec24b59c4b8971640345cc89d510bc5da3 100644 (file)
@@ -4,102 +4,105 @@ module PursLoader.Loader
   , loaderFn
   ) where
 
-import Prelude (Unit(), ($), (<>), (>>=), (<$>), (++), bind, flip, id, pure, return, unit)
+import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (++), bind, const, id, pure, unit)
 
-import Control.Monad.Aff (Aff(), runAff)
-import Control.Monad.Eff (Eff())
-import Control.Monad.Eff.Class (liftEff)
-import Control.Monad.Eff.Exception (error)
+import Control.Apply ((*>))
+import Control.Bind (join)
+import Control.Monad.Eff (Eff(), foreachE)
+import Control.Monad.Eff.Exception (Error(), error)
 
-import Data.Array ((!!), concat)
+import Data.Array ((!!))
+import Data.Bifunctor (lmap)
+import Data.Either (Either(..), either)
+import Data.Foreign.Class (read)
 import Data.Function (Fn2(), mkFn2)
-import Data.Maybe (Maybe(..), fromMaybe, maybe)
-import Data.String (joinWith)
-import Data.String.Regex (match, noFlags, regex, test)
-import Data.Traversable (sequence)
-
-import PursLoader.ChildProcess (ChildProcess(), spawn)
-import PursLoader.FS (FS(), writeFileUtf8, findFileUtf8)
-import PursLoader.Glob (Glob(), globAll)
-import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, query, clearDependencies, addDependency, resourcePath)
+import Data.Maybe (Maybe(..), maybe)
+import Data.Nullable (toMaybe)
+import Data.String.Regex (Regex(), match, noFlags, regex)
+
+import Unsafe.Coerce (unsafeCoerce)
+
+import PursLoader.LoaderRef
+  ( AsyncCallback()
+  , LoaderRef()
+  , Loader()
+  , async
+  , cacheable
+  , query
+  , clearDependencies
+  , addDependency
+  , resourcePath
+  )
+
+import PursLoader.Debug (debug)
 import PursLoader.LoaderUtil (parseQuery)
-import PursLoader.Options (loaderFFIOption, loaderSrcOption, pscOptions)
+import PursLoader.Options (Options(..))
+import PursLoader.Path (dirname, relative)
+import PursLoader.Plugin as Plugin
 
-type Effects eff = (cp :: ChildProcess, fs :: FS, glob :: Glob, loader :: Loader | eff)
+type Effects eff = (loader :: Loader | eff)
 
-moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
-
-foreignRegex = regex "(?:^|\\n)\\s*foreign import\\s+" noFlags { ignoreCase = true }
-
-pscCommand = "psc"
-
-psciCommand = "psci"
-
-psciFilename = ".psci"
-
-(!!!) = flip (!!)
-
-foreign import cwd :: String
-
-foreign import relative :: String -> String -> String
+loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit
+loader ref source = do
+  callback <- async ref
 
-foreign import resolve :: String -> String
+  cacheable ref
 
-mkPsci :: Array (Array String) -> Array (Array String) -> String
-mkPsci srcs ffis = joinWith "\n" ((loadModule <$> concat srcs) <> (loadForeign <$> concat ffis))
-  where
-    loadModule :: String -> String
-    loadModule a = ":m " ++ relative cwd a
+  debug "Invoke PureScript plugin compilation"
 
-    loadForeign :: String -> String
-    loadForeign a = ":f " ++ relative cwd a
-
-findFFI :: forall eff. Array (Array String) -> String -> Aff (fs :: FS | eff) (Maybe String)
-findFFI ffiss name = findFileUtf8 re (concat ffiss)
+  pluginContext.compile (compile callback)
   where
-    re = regex ("(?:^|\\n)//\\s*module\\s*" ++ name ++ "\\s*\\n") noFlags
-
-loader' :: forall eff. LoaderRef -> String -> Aff (Effects eff) (Maybe String)
-loader' ref source = do
-  liftEff $ cacheable ref
-
-  let parsed = parseQuery $ query ref
-      srcs = fromMaybe [] (loaderSrcOption parsed)
-      ffis = fromMaybe [] (loaderFFIOption parsed)
-      opts = pscOptions parsed
-
-  srcss <- globAll srcs
-  ffiss <- globAll ffis
-
-  let psciFile = mkPsci srcss ffiss
-
-  writeFileUtf8 psciFilename psciFile
-
-  let moduleName = match moduleRegex source >>= (!!!) 1 >>= id
-      hasForeign = test foreignRegex source
-      result = (\a -> "module.exports = require('" ++ a ++ "');") <$> moduleName
-
-  liftEff (clearDependencies ref)
-  liftEff (addDependency ref (resourcePath ref))
-  liftEff (sequence $ (\src -> addDependency ref (resolve src)) <$> concat srcss)
-
-  foreignPath <- if hasForeign
-                    then fromMaybe (pure Nothing) (findFFI ffiss <$> moduleName)
-                    else pure Nothing
-
-  fromMaybe (pure unit) ((\path -> liftEff (addDependency ref path)) <$> foreignPath)
-
-  spawn pscCommand (srcs <> opts)
-
-  return result
-
-loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit
-loader ref source = do
-  callback <- async ref
-  runAff (\e -> callback (Just e) "")
-         (maybe (callback (Just (error "Loader has failed to run")) "")
-                (callback Nothing))
-         (loader' ref source)
+  pluginContext :: Plugin.Context (Effects eff)
+  pluginContext = (unsafeCoerce ref).purescriptWebpackPluginContext
+
+  compile :: AsyncCallback eff -> Plugin.Compile (Effects eff)
+  compile callback error' { srcMap, ffiMap, graph } = do
+    clearDependencies ref
+
+    either (const $ pure unit) (\a -> debug ("Adding PureScript dependency " ++ a)) name
+
+    addDependency ref (resourcePath ref)
+
+    either (\err -> callback (Just err) "") id
+           (handle <$> name <*> dependencies <*> exports)
+    where
+    handle :: String -> Array String -> String -> Eff (Effects eff) Unit
+    handle name' deps res = do
+      debug ("Adding PureScript transitive dependencies for " ++ name')
+      addTransitive name'
+      foreachE deps addTransitive
+      callback (toMaybe error') res
+
+    exports :: Either Error String
+    exports = (\a b -> "module.exports = require('" ++ a ++ "')['" ++ b ++ "'];") <$> path <*> name
+
+    dependencies :: Either Error (Array String)
+    dependencies = name >>= Plugin.dependenciesOf graph
+
+    addTransitive :: String -> Eff (Effects eff) Unit
+    addTransitive dep = addDep (Plugin.get srcMap dep) *> addDep (Plugin.get ffiMap dep)
+      where
+      addDep :: Maybe String -> Eff (Effects eff) Unit
+      addDep = maybe (pure unit) (addDependency ref)
+
+    name :: Either Error String
+    name =
+      maybe (Left $ error "Failed to parse module name") Right
+            (join $ match re source >>= \as -> as !! 1)
+      where
+      re :: Regex
+      re = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
+
+    path :: Either Error String
+    path = (\(Options opts) -> relative resourceDir opts.bundleOutput) <$> options
+      where
+      options :: Either Error Options
+      options =
+        lmap (const $ error "Failed to parse loader query")
+             (read $ parseQuery (query ref))
+
+      resourceDir :: String
+      resourceDir = dirname (resourcePath ref)
 
 loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit)
 loaderFn = mkFn2 loader