diff options
Diffstat (limited to 'src/Loader.purs')
-rw-r--r-- | src/Loader.purs | 114 |
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 @@ | |||
1 | module PursLoader.Loader | ||
2 | ( LoaderEff() | ||
3 | , loader | ||
4 | , loaderFn | ||
5 | ) where | ||
6 | |||
7 | import Control.Monad.Aff (Aff(), runAff) | ||
8 | import Control.Monad.Eff (Eff()) | ||
9 | import Control.Monad.Eff.Class (liftEff) | ||
10 | import Control.Monad.Eff.Exception (error) | ||
11 | |||
12 | import Data.Array ((!!), catMaybes, concat, nub, null) | ||
13 | import Data.Function (Fn2(), mkFn2) | ||
14 | import Data.Maybe (Maybe(..), fromMaybe, maybe) | ||
15 | import Data.String (joinWith, split) | ||
16 | import Data.String.Regex (Regex(), match, noFlags, regex) | ||
17 | import Data.StrMap (StrMap(), fromList, lookup) | ||
18 | import Data.Traversable (sequence) | ||
19 | import Data.Tuple.Nested (tuple2) | ||
20 | |||
21 | import PursLoader.ChildProcess (ChildProcess(), spawn) | ||
22 | import PursLoader.FS (FS(), readFileUtf8, readFileUtf8Sync) | ||
23 | import PursLoader.Glob (Glob(), glob) | ||
24 | import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, clearDependencies, addDependency, query, resourcePath) | ||
25 | import PursLoader.LoaderUtil (getRemainingRequest, parseQuery) | ||
26 | import PursLoader.OS (eol) | ||
27 | import PursLoader.Options (pscMakeOptions, pscMakeDefaultOutput, pscMakeOutputOption) | ||
28 | import PursLoader.Path (dirname, join, relative, resolve) | ||
29 | |||
30 | foreign import cwd "var cwd = process.cwd();" :: String | ||
31 | |||
32 | moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true } | ||
33 | |||
34 | importRegex = regex "^\\s*import\\s+(?:qualified\\s+)?([\\w\\.]+)" noFlags { ignoreCase = true } | ||
35 | |||
36 | bowerPattern = join [ "bower_components", "purescript-*", "src" ] | ||
37 | |||
38 | pscMakeCommand = "psc-make" | ||
39 | |||
40 | indexFilename = "index.js" | ||
41 | |||
42 | (!!!) = flip (!!) | ||
43 | |||
44 | pursPattern :: String -> String | ||
45 | pursPattern root = join [ "{" ++ joinWith "," [ bowerPattern, root ] ++ "}" | ||
46 | , "**" | ||
47 | , "*.purs" | ||
48 | ] | ||
49 | |||
50 | type GraphModule = { file :: String, imports :: [String] } | ||
51 | |||
52 | type Graph = StrMap GraphModule | ||
53 | |||
54 | mkGraph :: forall eff. [String] -> Eff (fs :: FS | eff) Graph | ||
55 | mkGraph 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 | |||
62 | mkDeps :: forall eff. String -> Graph -> [String] | ||
63 | mkDeps 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 | |||
69 | addDeps :: forall eff. LoaderRef -> Graph -> [String] -> Eff (loader :: Loader | eff) Unit | ||
70 | addDeps 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 | |||
75 | type LoaderAff eff a = Aff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a | ||
76 | |||
77 | loader' :: forall eff. LoaderRef -> String -> LoaderAff eff (Maybe String) | ||
78 | loader' 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 | |||
103 | type LoaderEff eff a = Eff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a | ||
104 | |||
105 | loader :: forall eff. LoaderRef -> String -> LoaderEff eff Unit | ||
106 | loader 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 | |||
113 | loaderFn :: forall eff. Fn2 LoaderRef String (LoaderEff eff Unit) | ||
114 | loaderFn = mkFn2 loader | ||