diff options
-rw-r--r-- | bower.json | 4 | ||||
-rw-r--r-- | docs/PursLoader/Options.md | 1 | ||||
-rw-r--r-- | docs/PursLoader/Plugin.md | 45 | ||||
-rw-r--r-- | example/src/Foo.purs | 6 | ||||
-rw-r--r-- | example/src/Foo/Bar.purs | 12 | ||||
-rw-r--r-- | example/src/Foo/Boz.purs | 5 | ||||
-rw-r--r-- | example/src/FooFFI.js | 5 | ||||
-rw-r--r-- | src/PursLoader/Loader.purs | 92 | ||||
-rw-r--r-- | src/PursLoader/Options.purs | 2 | ||||
-rw-r--r-- | src/PursLoader/Plugin.js | 20 | ||||
-rw-r--r-- | src/PursLoader/Plugin.purs | 49 |
11 files changed, 202 insertions, 39 deletions
@@ -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 |
6 | newtype Options | 6 | newtype 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 | ||
6 | type Result = { srcMap :: ImmutableMap String String, ffiMap :: ImmutableMap String String, graph :: DependencyGraph } | ||
7 | ``` | ||
8 | |||
9 | #### `Compile` | ||
10 | |||
11 | ``` purescript | ||
12 | type Compile eff = Nullable Error -> Result -> Eff eff Unit | ||
13 | ``` | ||
14 | |||
15 | #### `Context` | ||
16 | |||
17 | ``` purescript | ||
18 | type Context eff = { compile :: Compile eff -> Eff eff Unit } | ||
19 | ``` | ||
20 | |||
21 | #### `get` | ||
22 | |||
23 | ``` purescript | ||
24 | get :: forall key value. ImmutableMap key value -> key -> Maybe value | ||
25 | ``` | ||
26 | |||
27 | #### `dependenciesOf` | ||
28 | |||
29 | ``` purescript | ||
30 | dependenciesOf :: DependencyGraph -> String -> Either Error (Array String) | ||
31 | ``` | ||
32 | |||
33 | #### `ImmutableMap` | ||
34 | |||
35 | ``` purescript | ||
36 | data ImmutableMap :: * -> * -> * | ||
37 | ``` | ||
38 | |||
39 | #### `DependencyGraph` | ||
40 | |||
41 | ``` purescript | ||
42 | data DependencyGraph :: * | ||
43 | ``` | ||
44 | |||
45 | |||
diff --git a/example/src/Foo.purs b/example/src/Foo.purs index 97b7cec..a715be0 100644 --- a/example/src/Foo.purs +++ b/example/src/Foo.purs | |||
@@ -7,3 +7,9 @@ import qualified Foo.Bar as B | |||
7 | foo = "A" | 7 | foo = "A" |
8 | 8 | ||
9 | bar = "B" | 9 | bar = "B" |
10 | |||
11 | jaz = "D" ++ B.jar ++ B.jee | ||
12 | |||
13 | foreign import foot :: String | ||
14 | |||
15 | foreign import meter :: String | ||
diff --git a/example/src/Foo/Bar.purs b/example/src/Foo/Bar.purs index 6932902..1b0919c 100644 --- a/example/src/Foo/Bar.purs +++ b/example/src/Foo/Bar.purs | |||
@@ -1,5 +1,13 @@ | |||
1 | module Foo.Bar (bar) where | 1 | module Foo.Bar (bar, jar, jee) where |
2 | 2 | ||
3 | bar = "c" | 3 | import Foo.Boz |
4 | |||
5 | bar = "u" | ||
4 | 6 | ||
5 | bar' = "d" | 7 | bar' = "d" |
8 | |||
9 | jam = "c" | ||
10 | |||
11 | jar = "xyzw" | ||
12 | |||
13 | jee = "abcde" | ||
diff --git a/example/src/Foo/Boz.purs b/example/src/Foo/Boz.purs new file mode 100644 index 0000000..c802e47 --- /dev/null +++ b/example/src/Foo/Boz.purs | |||
@@ -0,0 +1,5 @@ | |||
1 | module Foo.Boz where | ||
2 | |||
3 | a = "a" | ||
4 | |||
5 | b = "b" | ||
diff --git a/example/src/FooFFI.js b/example/src/FooFFI.js new file mode 100644 index 0000000..a2681c8 --- /dev/null +++ b/example/src/FooFFI.js | |||
@@ -0,0 +1,5 @@ | |||
1 | // module Foo | ||
2 | |||
3 | exports.foot = "a"; | ||
4 | |||
5 | exports.meter = "b"; | ||
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 | ||
7 | import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (<<<), (++), bind, const) | 7 | import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (++), bind, const, id, pure, unit) |
8 | 8 | ||
9 | import Control.Apply ((*>)) | ||
9 | import Control.Bind (join) | 10 | import Control.Bind (join) |
10 | import Control.Monad.Eff (Eff()) | 11 | import Control.Monad.Eff (Eff(), foreachE) |
11 | import Control.Monad.Eff.Exception (Error(), error) | 12 | import Control.Monad.Eff.Exception (Error(), error) |
12 | 13 | ||
13 | import Data.Array ((!!)) | 14 | import Data.Array ((!!)) |
15 | import Data.Bifunctor (lmap) | ||
16 | import Data.Either (Either(..), either) | ||
17 | import Data.Foreign.Class (read) | ||
14 | import Data.Function (Fn2(), mkFn2) | 18 | import Data.Function (Fn2(), mkFn2) |
15 | import Data.Maybe (Maybe(..), maybe) | 19 | import Data.Maybe (Maybe(..), maybe) |
16 | import Data.Either (either) | 20 | import Data.Nullable (toMaybe) |
17 | import Data.Foreign (Foreign()) | ||
18 | import Data.Foreign.Class (read) | ||
19 | import Data.Foreign.Null (runNull) | ||
20 | import Data.String.Regex (Regex(), match, noFlags, regex) | 21 | import Data.String.Regex (Regex(), match, noFlags, regex) |
21 | 22 | ||
22 | import Unsafe.Coerce (unsafeCoerce) | 23 | import Unsafe.Coerce (unsafeCoerce) |
23 | 24 | ||
24 | import PursLoader.LoaderRef | 25 | import 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 | ||
35 | import PursLoader.LoaderUtil (parseQuery) | 37 | import PursLoader.LoaderUtil (parseQuery) |
36 | import PursLoader.Options (runOptions) | 38 | import PursLoader.Options (Options(..)) |
37 | import PursLoader.Path (dirname, relative) | 39 | import PursLoader.Path (dirname, relative) |
40 | import PursLoader.Plugin as Plugin | ||
38 | 41 | ||
39 | type Effects eff = (loader :: Loader | eff) | 42 | type Effects eff = (loader :: Loader | eff) |
40 | 43 | ||
41 | type PurescriptWebpackPluginContext eff = { compile :: (Foreign -> Eff (Effects eff) Unit) -> Eff (Effects eff) Unit } | ||
42 | |||
43 | loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit | 44 | loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit |
44 | loader ref source = do | 45 | loader 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 | ||
77 | loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit) | 101 | loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit) |
78 | loaderFn = mkFn2 loader | 102 | loaderFn = 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 @@ | |||
1 | module PursLoader.Options | 1 | module 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 | |||
5 | function getFn(nothing, just, map, key) { | ||
6 | var value = map.get(key); | ||
7 | return value === undefined ? nothing : just(value); | ||
8 | } | ||
9 | exports.getFn = getFn; | ||
10 | |||
11 | function 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 | } | ||
20 | exports.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 @@ | |||
1 | module PursLoader.Plugin | ||
2 | ( Result() | ||
3 | , Compile() | ||
4 | , Context() | ||
5 | , ImmutableMap() | ||
6 | , DependencyGraph() | ||
7 | , get | ||
8 | , dependenciesOf | ||
9 | ) where | ||
10 | |||
11 | import Prelude (Unit()) | ||
12 | |||
13 | import Control.Monad.Eff (Eff()) | ||
14 | import Control.Monad.Eff.Exception (Error()) | ||
15 | |||
16 | import Data.Either (Either(..)) | ||
17 | import Data.Function (Fn4(), runFn4) | ||
18 | import Data.Maybe (Maybe(..)) | ||
19 | import Data.Nullable (Nullable()) | ||
20 | |||
21 | type Result = { srcMap :: ImmutableMap String String, ffiMap :: ImmutableMap String String, graph :: DependencyGraph } | ||
22 | |||
23 | type Compile eff = Nullable Error -> Result -> Eff eff Unit | ||
24 | |||
25 | type Context eff = { compile :: Compile eff -> Eff eff Unit } | ||
26 | |||
27 | get :: forall key value. ImmutableMap key value -> key -> Maybe value | ||
28 | get = runFn4 getFn Nothing Just | ||
29 | |||
30 | dependenciesOf :: DependencyGraph -> String -> Either Error (Array String) | ||
31 | dependenciesOf = runFn4 dependenciesOfFn Left Right | ||
32 | |||
33 | foreign import data ImmutableMap :: * -> * -> * | ||
34 | |||
35 | foreign import data DependencyGraph :: * | ||
36 | |||
37 | foreign import getFn | ||
38 | :: forall key value. Fn4 (Maybe value) | ||
39 | (value -> Maybe value) | ||
40 | (ImmutableMap key value) | ||
41 | key | ||
42 | (Maybe value) | ||
43 | |||
44 | foreign 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)) | ||