, loaderFn
) where
-import Prelude (Unit(), ($), (<>), (>>=), (<$>), (++), bind, flip, id, pure, return, unit, show)
+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 (throwException, error, EXCEPTION())
+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.Function (Fn2(), mkFn2)
-import Data.Maybe (Maybe(..), fromMaybe, maybe)
-import Data.Either (Either(..))
-import Data.String (joinWith)
-import Data.String.Regex (Regex(), match, noFlags, regex, test)
-import Data.Traversable (sequence)
-import Data.Foreign (F())
+import Data.Array ((!!))
+import Data.Bifunctor (lmap)
+import Data.Either (Either(..), either)
import Data.Foreign.Class (read)
-
-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.Function (Fn2(), mkFn2)
+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, Options(), output)
-
-type Effects eff = (cp :: ChildProcess, fs :: FS, glob :: Glob, loader :: Loader, err :: EXCEPTION | eff)
-
-moduleRegex :: Regex
-moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
-
-foreignRegex :: Regex
-foreignRegex = regex "(?:^|\\n)\\s*foreign import\\s+" noFlags { ignoreCase = true }
-
-pscCommand :: String
-pscCommand = "psc"
-
-psciCommand :: String
-psciCommand = "psci"
-
-psciFilename :: String
-psciFilename = ".psci"
+import PursLoader.Options (Options(..))
+import PursLoader.Path (dirname, relative)
+import PursLoader.Plugin as Plugin
-(!!!) :: forall a. Int -> Array a -> Maybe a
-(!!!) = flip (!!)
+type Effects eff = (loader :: Loader | eff)
-foreign import cwd :: String
-
-foreign import relative :: String -> String -> String
-
-foreign import resolve :: String -> String
-
-foreign import dirname :: String -> String
-
-foreign import joinPath :: String -> String -> String
+loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit
+loader ref source = do
+ callback <- async 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
+ cacheable ref
- loadForeign :: String -> String
- loadForeign a = ":f " ++ relative cwd a
+ debug "Invoke PureScript plugin compilation"
-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)
-
- case read parsed :: F Options of
- Left e -> liftEff (throwException (error (show e)))
- Right opts -> do
- let pscOpts = pscOptions opts
-
- 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
- outputDir = resolve (output opts)
- resourceDir = dirname (resourcePath ref)
- result = (\a -> "module.exports = require('" ++ relative resourceDir (joinPath outputDir a) ++ "');") <$> moduleName
-
- liftEff do
- clearDependencies ref
- addDependency ref (resourcePath ref)
- 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 <> pscOpts)
-
- 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