]> git.immae.eu Git - github/fretlink/purs-loader.git/blob - src/PursLoader/Loader.purs
Merge pull request #38 from ethul/topic/dependency-graph
[github/fretlink/purs-loader.git] / src / PursLoader / Loader.purs
1 module PursLoader.Loader
2 ( Effects()
3 , loader
4 , loaderFn
5 ) where
6
7 import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (++), bind, const, id, pure, unit)
8
9 import Control.Apply ((*>))
10 import Control.Bind (join)
11 import Control.Monad.Eff (Eff(), foreachE)
12 import Control.Monad.Eff.Exception (Error(), error)
13
14 import Data.Array ((!!))
15 import Data.Bifunctor (lmap)
16 import Data.Either (Either(..), either)
17 import Data.Foreign.Class (read)
18 import Data.Function (Fn2(), mkFn2)
19 import Data.Maybe (Maybe(..), maybe)
20 import Data.Nullable (toMaybe)
21 import Data.String.Regex (Regex(), match, noFlags, regex)
22
23 import Unsafe.Coerce (unsafeCoerce)
24
25 import PursLoader.LoaderRef
26 ( AsyncCallback()
27 , LoaderRef()
28 , Loader()
29 , async
30 , cacheable
31 , query
32 , clearDependencies
33 , addDependency
34 , resourcePath
35 )
36
37 import PursLoader.LoaderUtil (parseQuery)
38 import PursLoader.Options (Options(..))
39 import PursLoader.Path (dirname, relative)
40 import PursLoader.Plugin as Plugin
41
42 type Effects eff = (loader :: Loader | eff)
43
44 loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit
45 loader ref source = do
46 callback <- async ref
47
48 cacheable ref
49
50 pluginContext.compile (compile callback)
51 where
52 pluginContext :: Plugin.Context (Effects eff)
53 pluginContext = (unsafeCoerce ref).purescriptWebpackPluginContext
54
55 compile :: AsyncCallback eff -> Plugin.Compile (Effects eff)
56 compile callback error' { srcMap, ffiMap, graph } = do
57 clearDependencies ref
58
59 addDependency ref (resourcePath ref)
60
61 either (\err -> callback (Just err) "") id
62 (handle <$> name <*> dependencies <*> exports)
63 where
64 handle :: String -> Array String -> String -> Eff (Effects eff) Unit
65 handle name' deps res = do
66 addTransitive name'
67 foreachE deps addTransitive
68 callback (toMaybe error') res
69
70 exports :: Either Error String
71 exports = (\a b -> "module.exports = require('" ++ a ++ "')['" ++ b ++ "'];") <$> path <*> name
72
73 dependencies :: Either Error (Array String)
74 dependencies = name >>= Plugin.dependenciesOf graph
75
76 addTransitive :: String -> Eff (Effects eff) Unit
77 addTransitive dep = addDep (Plugin.get srcMap dep) *> addDep (Plugin.get ffiMap dep)
78 where
79 addDep :: Maybe String -> Eff (Effects eff) Unit
80 addDep = maybe (pure unit) (addDependency ref)
81
82 name :: Either Error String
83 name =
84 maybe (Left $ error "Failed to parse module name") Right
85 (join $ match re source >>= \as -> as !! 1)
86 where
87 re :: Regex
88 re = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
89
90 path :: Either Error String
91 path = (\(Options opts) -> relative resourceDir opts.bundleOutput) <$> options
92 where
93 options :: Either Error Options
94 options =
95 lmap (const $ error "Failed to parse loader query")
96 (read $ parseQuery (query ref))
97
98 resourceDir :: String
99 resourceDir = dirname (resourcePath ref)
100
101 loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit)
102 loaderFn = mkFn2 loader