aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Loader.purs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Loader.purs')
-rw-r--r--src/Loader.purs114
1 files changed, 114 insertions, 0 deletions
diff --git a/src/Loader.purs b/src/Loader.purs
new file mode 100644
index 0000000..523aa7a
--- /dev/null
+++ b/src/Loader.purs
@@ -0,0 +1,114 @@
1module PursLoader.Loader
2 ( LoaderEff()
3 , loader
4 , loaderFn
5 ) where
6
7import Control.Monad.Aff (Aff(), runAff)
8import Control.Monad.Eff (Eff())
9import Control.Monad.Eff.Class (liftEff)
10import Control.Monad.Eff.Exception (error)
11
12import Data.Array ((!!), catMaybes, concat, nub, null)
13import Data.Function (Fn2(), mkFn2)
14import Data.Maybe (Maybe(..), fromMaybe, maybe)
15import Data.String (joinWith, split)
16import Data.String.Regex (Regex(), match, noFlags, regex)
17import Data.StrMap (StrMap(), fromList, lookup)
18import Data.Traversable (sequence)
19import Data.Tuple.Nested (tuple2)
20
21import PursLoader.ChildProcess (ChildProcess(), spawn)
22import PursLoader.FS (FS(), readFileUtf8, readFileUtf8Sync)
23import PursLoader.Glob (Glob(), glob)
24import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, clearDependencies, addDependency, query, resourcePath)
25import PursLoader.LoaderUtil (getRemainingRequest, parseQuery)
26import PursLoader.OS (eol)
27import PursLoader.Options (pscMakeOptions, pscMakeDefaultOutput, pscMakeOutputOption)
28import PursLoader.Path (dirname, join, relative, resolve)
29
30foreign import cwd "var cwd = process.cwd();" :: String
31
32moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
33
34importRegex = regex "^\\s*import\\s+(?:qualified\\s+)?([\\w\\.]+)" noFlags { ignoreCase = true }
35
36bowerPattern = join [ "bower_components", "purescript-*", "src" ]
37
38pscMakeCommand = "psc-make"
39
40indexFilename = "index.js"
41
42(!!!) = flip (!!)
43
44pursPattern :: String -> String
45pursPattern root = join [ "{" ++ joinWith "," [ bowerPattern, root ] ++ "}"
46 , "**"
47 , "*.purs"
48 ]
49
50type GraphModule = { file :: String, imports :: [String] }
51
52type Graph = StrMap GraphModule
53
54mkGraph :: forall eff. [String] -> Eff (fs :: FS | eff) Graph
55mkGraph files = (fromList <<< catMaybes) <$> sequence (parse <$> files)
56 where parse file = do source <- readFileUtf8Sync file
57 let key = match moduleRegex source >>= (!!!) 1
58 lines = split eol source
59 imports = catMaybes $ (\a -> match importRegex a >>= (!!!) 1) <$> lines
60 return $ (\a -> tuple2 a { file: file, imports: imports }) <$> key
61
62mkDeps :: forall eff. String -> Graph -> [String]
63mkDeps key graph = nub $ go [] key
64 where go acc key =
65 maybe acc (\a -> if null a.imports
66 then acc
67 else concat $ go (acc <> a.imports) <$> a.imports) (lookup key graph)
68
69addDeps :: forall eff. LoaderRef -> Graph -> [String] -> Eff (loader :: Loader | eff) Unit
70addDeps ref graph deps = const unit <$> (sequence $ add <$> deps)
71 where add dep = let res = lookup dep graph
72 path = (\a -> resolve a.file) <$> res
73 in maybe (pure unit) (addDependency ref) path
74
75type LoaderAff eff a = Aff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
76
77loader' :: forall eff. LoaderRef -> String -> LoaderAff eff (Maybe String)
78loader' ref source = do
79 liftEff $ cacheable ref
80
81 let request = getRemainingRequest ref
82 root = dirname $ relative cwd request
83 parsed = parseQuery $ query ref
84 opts = pscMakeOptions parsed
85 pattern = pursPattern root
86 key = match moduleRegex source >>= (!!!) 1
87
88 files <- glob pattern
89 graph <- liftEff $ mkGraph files
90
91 let deps = fromMaybe [] $ flip mkDeps graph <$> key
92 outputPath = fromMaybe pscMakeDefaultOutput $ pscMakeOutputOption parsed
93 indexPath = (\a -> join [ outputPath, a, indexFilename ]) <$> key
94
95 liftEff $ clearDependencies ref
96 liftEff $ addDependency ref (resourcePath ref)
97 liftEff $ addDeps ref graph deps
98
99 spawn pscMakeCommand (opts <> files)
100 indexFile <- sequence $ readFileUtf8 <$> indexPath
101 return indexFile
102
103type LoaderEff eff a = Eff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
104
105loader :: forall eff. LoaderRef -> String -> LoaderEff eff Unit
106loader ref source = do
107 callback <- async ref
108 runAff (\e -> callback (Just e) "")
109 (maybe (callback (Just $ error "Loader has failed to run") "")
110 (callback Nothing))
111 (loader' ref source)
112
113loaderFn :: forall eff. Fn2 LoaderRef String (LoaderEff eff Unit)
114loaderFn = mkFn2 loader