aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoreric thul <thul.eric@gmail.com>2016-02-24 22:53:40 -0500
committereric thul <thul.eric@gmail.com>2016-02-24 22:53:40 -0500
commit460393439a8dd7d94fe02a2013e96c9146282bf1 (patch)
tree178492f39cc5afa716f1c403d4a28d45606336a3
parentef1525b5a0fe3f4561493aaca49907dba827dbd6 (diff)
downloadpurs-loader-460393439a8dd7d94fe02a2013e96c9146282bf1.tar.gz
purs-loader-460393439a8dd7d94fe02a2013e96c9146282bf1.tar.zst
purs-loader-460393439a8dd7d94fe02a2013e96c9146282bf1.zip
Adds dependencies of modules process by the loader.
The module file path and dependency graph information is provided by the purescript-webpack-plugin. Resolves #37
-rw-r--r--bower.json4
-rw-r--r--docs/PursLoader/Options.md1
-rw-r--r--docs/PursLoader/Plugin.md45
-rw-r--r--src/PursLoader/Loader.purs92
-rw-r--r--src/PursLoader/Options.purs2
-rw-r--r--src/PursLoader/Plugin.js20
-rw-r--r--src/PursLoader/Plugin.purs49
7 files changed, 176 insertions, 37 deletions
diff --git a/bower.json b/bower.json
index 8b61f6f..761c24c 100644
--- a/bower.json
+++ b/bower.json
@@ -3,8 +3,8 @@
3 "private": true, 3 "private": true,
4 "dependencies": { 4 "dependencies": {
5 "purescript-aff": "^0.13.0", 5 "purescript-aff": "^0.13.0",
6 "purescript-strings": "^0.7.0",
7 "purescript-foreign": "^0.7.0", 6 "purescript-foreign": "^0.7.0",
8 "purescript-unsafe-coerce": "~0.1.0" 7 "purescript-unsafe-coerce": "~0.1.0",
8 "purescript-nullable": "~0.2.1"
9 } 9 }
10} 10}
diff --git a/docs/PursLoader/Options.md b/docs/PursLoader/Options.md
index 2bfcddd..b3352fc 100644
--- a/docs/PursLoader/Options.md
+++ b/docs/PursLoader/Options.md
@@ -4,6 +4,7 @@
4 4
5``` purescript 5``` purescript
6newtype Options 6newtype Options
7 = Options { bundleOutput :: String }
7``` 8```
8 9
9##### Instances 10##### Instances
diff --git a/docs/PursLoader/Plugin.md b/docs/PursLoader/Plugin.md
new file mode 100644
index 0000000..9abec4d
--- /dev/null
+++ b/docs/PursLoader/Plugin.md
@@ -0,0 +1,45 @@
1## Module PursLoader.Plugin
2
3#### `Result`
4
5``` purescript
6type Result = { srcMap :: ImmutableMap String String, ffiMap :: ImmutableMap String String, graph :: DependencyGraph }
7```
8
9#### `Compile`
10
11``` purescript
12type Compile eff = Nullable Error -> Result -> Eff eff Unit
13```
14
15#### `Context`
16
17``` purescript
18type Context eff = { compile :: Compile eff -> Eff eff Unit }
19```
20
21#### `get`
22
23``` purescript
24get :: forall key value. ImmutableMap key value -> key -> Maybe value
25```
26
27#### `dependenciesOf`
28
29``` purescript
30dependenciesOf :: DependencyGraph -> String -> Either Error (Array String)
31```
32
33#### `ImmutableMap`
34
35``` purescript
36data ImmutableMap :: * -> * -> *
37```
38
39#### `DependencyGraph`
40
41``` purescript
42data DependencyGraph :: *
43```
44
45
diff --git a/src/PursLoader/Loader.purs b/src/PursLoader/Loader.purs
index affce53..f78153f 100644
--- a/src/PursLoader/Loader.purs
+++ b/src/PursLoader/Loader.purs
@@ -4,25 +4,27 @@ module PursLoader.Loader
4 , loaderFn 4 , loaderFn
5 ) where 5 ) where
6 6
7import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (<<<), (++), bind, const) 7import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (++), bind, const, id, pure, unit)
8 8
9import Control.Apply ((*>))
9import Control.Bind (join) 10import Control.Bind (join)
10import Control.Monad.Eff (Eff()) 11import Control.Monad.Eff (Eff(), foreachE)
11import Control.Monad.Eff.Exception (Error(), error) 12import Control.Monad.Eff.Exception (Error(), error)
12 13
13import Data.Array ((!!)) 14import Data.Array ((!!))
15import Data.Bifunctor (lmap)
16import Data.Either (Either(..), either)
17import Data.Foreign.Class (read)
14import Data.Function (Fn2(), mkFn2) 18import Data.Function (Fn2(), mkFn2)
15import Data.Maybe (Maybe(..), maybe) 19import Data.Maybe (Maybe(..), maybe)
16import Data.Either (either) 20import Data.Nullable (toMaybe)
17import Data.Foreign (Foreign())
18import Data.Foreign.Class (read)
19import Data.Foreign.Null (runNull)
20import Data.String.Regex (Regex(), match, noFlags, regex) 21import Data.String.Regex (Regex(), match, noFlags, regex)
21 22
22import Unsafe.Coerce (unsafeCoerce) 23import Unsafe.Coerce (unsafeCoerce)
23 24
24import PursLoader.LoaderRef 25import PursLoader.LoaderRef
25 ( LoaderRef() 26 ( AsyncCallback()
27 , LoaderRef()
26 , Loader() 28 , Loader()
27 , async 29 , async
28 , cacheable 30 , cacheable
@@ -33,46 +35,68 @@ import PursLoader.LoaderRef
33 ) 35 )
34 36
35import PursLoader.LoaderUtil (parseQuery) 37import PursLoader.LoaderUtil (parseQuery)
36import PursLoader.Options (runOptions) 38import PursLoader.Options (Options(..))
37import PursLoader.Path (dirname, relative) 39import PursLoader.Path (dirname, relative)
40import PursLoader.Plugin as Plugin
38 41
39type Effects eff = (loader :: Loader | eff) 42type Effects eff = (loader :: Loader | eff)
40 43
41type PurescriptWebpackPluginContext eff = { compile :: (Foreign -> Eff (Effects eff) Unit) -> Eff (Effects eff) Unit }
42
43loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit 44loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit
44loader ref source = do 45loader ref source = do
45 callback <- async ref 46 callback <- async ref
46 47
47 cacheable ref 48 cacheable ref
48 49
49 let parsed = parseQuery $ query ref 50 pluginContext.compile (compile callback)
50
51 options = either (const Nothing) (Just <<< runOptions) (read parsed)
52
53 moduleName = join $ match moduleRegex source >>= \as -> as !! 1
54
55 resourceDir = dirname (resourcePath ref)
56
57 modulePath = (\opts -> relative resourceDir opts.bundleOutput) <$> options
58
59 result = (\path name -> "module.exports = require('" ++ path ++ "')['" ++ name ++ "'];") <$> modulePath <*> moduleName
60
61 clearDependencies ref
62
63 addDependency ref (resourcePath ref)
64
65 pluginContext.compile (\err -> maybe (callback (Just $ error "Failed to run loader") "")
66 (callback (compileError err)) result)
67 where 51 where
68 moduleRegex :: Regex 52 pluginContext :: Plugin.Context (Effects eff)
69 moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
70
71 pluginContext :: PurescriptWebpackPluginContext eff
72 pluginContext = (unsafeCoerce ref).purescriptWebpackPluginContext 53 pluginContext = (unsafeCoerce ref).purescriptWebpackPluginContext
73 54
74 compileError :: Foreign -> Maybe Error 55 compile :: AsyncCallback eff -> Plugin.Compile (Effects eff)
75 compileError value = either (const $ Just (error "Failed to compile")) ((<$>) error) (runNull <$> read value) 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)
76 100
77loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit) 101loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit)
78loaderFn = mkFn2 loader 102loaderFn = mkFn2 loader
diff --git a/src/PursLoader/Options.purs b/src/PursLoader/Options.purs
index 706ddd2..0c1453e 100644
--- a/src/PursLoader/Options.purs
+++ b/src/PursLoader/Options.purs
@@ -1,5 +1,5 @@
1module PursLoader.Options 1module PursLoader.Options
2 ( Options() 2 ( Options(..)
3 , runOptions 3 , runOptions
4 ) where 4 ) where
5 5
diff --git a/src/PursLoader/Plugin.js b/src/PursLoader/Plugin.js
new file mode 100644
index 0000000..90feb33
--- /dev/null
+++ b/src/PursLoader/Plugin.js
@@ -0,0 +1,20 @@
1'use strict';
2
3// module PursLoader.Plugin
4
5function getFn(nothing, just, map, key) {
6 var value = map.get(key);
7 return value === undefined ? nothing : just(value);
8}
9exports.getFn = getFn;
10
11function dependenciesOfFn(left, right, graph, node) {
12 try {
13 var dependencies = graph.dependenciesOf(node);
14 return right(dependencies);
15 }
16 catch (error) {
17 return left(error);
18 }
19}
20exports.dependenciesOfFn = dependenciesOfFn;
diff --git a/src/PursLoader/Plugin.purs b/src/PursLoader/Plugin.purs
new file mode 100644
index 0000000..23f8600
--- /dev/null
+++ b/src/PursLoader/Plugin.purs
@@ -0,0 +1,49 @@
1module PursLoader.Plugin
2 ( Result()
3 , Compile()
4 , Context()
5 , ImmutableMap()
6 , DependencyGraph()
7 , get
8 , dependenciesOf
9 ) where
10
11import Prelude (Unit())
12
13import Control.Monad.Eff (Eff())
14import Control.Monad.Eff.Exception (Error())
15
16import Data.Either (Either(..))
17import Data.Function (Fn4(), runFn4)
18import Data.Maybe (Maybe(..))
19import Data.Nullable (Nullable())
20
21type Result = { srcMap :: ImmutableMap String String, ffiMap :: ImmutableMap String String, graph :: DependencyGraph }
22
23type Compile eff = Nullable Error -> Result -> Eff eff Unit
24
25type Context eff = { compile :: Compile eff -> Eff eff Unit }
26
27get :: forall key value. ImmutableMap key value -> key -> Maybe value
28get = runFn4 getFn Nothing Just
29
30dependenciesOf :: DependencyGraph -> String -> Either Error (Array String)
31dependenciesOf = runFn4 dependenciesOfFn Left Right
32
33foreign import data ImmutableMap :: * -> * -> *
34
35foreign import data DependencyGraph :: *
36
37foreign import getFn
38 :: forall key value. Fn4 (Maybe value)
39 (value -> Maybe value)
40 (ImmutableMap key value)
41 key
42 (Maybe value)
43
44foreign import dependenciesOfFn
45 :: Fn4 (Error -> Either Error (Array String))
46 (Array String -> Either Error (Array String))
47 DependencyGraph
48 String
49 (Either Error (Array String))