]> git.immae.eu Git - github/fretlink/purs-loader.git/blame - src/Loader.purs
Additional loader options
[github/fretlink/purs-loader.git] / src / Loader.purs
CommitLineData
c194f84c 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
d997476f 12import Data.Array ((!!), catMaybes, concat, filter, null)
13import Data.Foldable (foldl)
c194f84c 14import Data.Function (Fn2(), mkFn2)
15import Data.Maybe (Maybe(..), fromMaybe, maybe)
d997476f 16import Data.Set (Set(), empty, insert, member, toList, unions)
c194f84c 17import Data.String (joinWith, split)
18import Data.String.Regex (Regex(), match, noFlags, regex)
19import Data.StrMap (StrMap(), fromList, lookup)
20import Data.Traversable (sequence)
21import Data.Tuple.Nested (tuple2)
22
23import PursLoader.ChildProcess (ChildProcess(), spawn)
24import PursLoader.FS (FS(), readFileUtf8, readFileUtf8Sync)
25import PursLoader.Glob (Glob(), glob)
26import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, clearDependencies, addDependency, query, resourcePath)
27import PursLoader.LoaderUtil (getRemainingRequest, parseQuery)
28import PursLoader.OS (eol)
a72c8af1 29import PursLoader.Options (loaderSrcOption, pscMakeOptions, pscMakeDefaultOutput, pscMakeOutputOption)
c194f84c 30import PursLoader.Path (dirname, join, relative, resolve)
31
32foreign import cwd "var cwd = process.cwd();" :: String
33
34moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
35
36importRegex = regex "^\\s*import\\s+(?:qualified\\s+)?([\\w\\.]+)" noFlags { ignoreCase = true }
37
38bowerPattern = join [ "bower_components", "purescript-*", "src" ]
39
40pscMakeCommand = "psc-make"
41
42indexFilename = "index.js"
43
44(!!!) = flip (!!)
45
a72c8af1 46pursPattern :: [String] -> String
47pursPattern srcs = join [ "{" ++ joinWith "," ([ bowerPattern ] <> srcs) ++ "}"
c194f84c 48 , "**"
49 , "*.purs"
50 ]
51
52type GraphModule = { file :: String, imports :: [String] }
53
54type Graph = StrMap GraphModule
55
56mkGraph :: forall eff. [String] -> Eff (fs :: FS | eff) Graph
57mkGraph files = (fromList <<< catMaybes) <$> sequence (parse <$> files)
58 where parse file = do source <- readFileUtf8Sync file
59 let key = match moduleRegex source >>= (!!!) 1
60 lines = split eol source
61 imports = catMaybes $ (\a -> match importRegex a >>= (!!!) 1) <$> lines
62 return $ (\a -> tuple2 a { file: file, imports: imports }) <$> key
63
64mkDeps :: forall eff. String -> Graph -> [String]
d997476f 65mkDeps key graph = toList $ go empty key
66 where
67 go :: Set String -> String -> Set String
68 go acc key =
69 let node = fromMaybe {file: "", imports: []} (lookup key graph)
70 uniq = filter (not <<< flip member acc) node.imports
71 acc' = foldl (flip insert) acc node.imports
72 in if null uniq
73 then acc'
74 else unions $ go acc' <$> uniq
c194f84c 75
76addDeps :: forall eff. LoaderRef -> Graph -> [String] -> Eff (loader :: Loader | eff) Unit
77addDeps ref graph deps = const unit <$> (sequence $ add <$> deps)
78 where add dep = let res = lookup dep graph
79 path = (\a -> resolve a.file) <$> res
80 in maybe (pure unit) (addDependency ref) path
81
82type LoaderAff eff a = Aff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
83
84loader' :: forall eff. LoaderRef -> String -> LoaderAff eff (Maybe String)
85loader' ref source = do
86 liftEff $ cacheable ref
87
88 let request = getRemainingRequest ref
c194f84c 89 parsed = parseQuery $ query ref
a72c8af1 90 srcs = loaderSrcOption parsed
c194f84c 91 opts = pscMakeOptions parsed
a72c8af1 92 pattern = pursPattern $ fromMaybe [] srcs
c194f84c 93 key = match moduleRegex source >>= (!!!) 1
94
95 files <- glob pattern
96 graph <- liftEff $ mkGraph files
97
98 let deps = fromMaybe [] $ flip mkDeps graph <$> key
99 outputPath = fromMaybe pscMakeDefaultOutput $ pscMakeOutputOption parsed
100 indexPath = (\a -> join [ outputPath, a, indexFilename ]) <$> key
101
102 liftEff $ clearDependencies ref
103 liftEff $ addDependency ref (resourcePath ref)
104 liftEff $ addDeps ref graph deps
105
106 spawn pscMakeCommand (opts <> files)
107 indexFile <- sequence $ readFileUtf8 <$> indexPath
108 return indexFile
109
110type LoaderEff eff a = Eff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
111
112loader :: forall eff. LoaderRef -> String -> LoaderEff eff Unit
113loader ref source = do
114 callback <- async ref
115 runAff (\e -> callback (Just e) "")
116 (maybe (callback (Just $ error "Loader has failed to run") "")
117 (callback Nothing))
118 (loader' ref source)
119
120loaderFn :: forall eff. Fn2 LoaderRef String (LoaderEff eff Unit)
121loaderFn = mkFn2 loader