aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/PursLoader
diff options
context:
space:
mode:
Diffstat (limited to 'src/PursLoader')
-rw-r--r--src/PursLoader/ChildProcess.purs56
-rw-r--r--src/PursLoader/FS.purs67
-rw-r--r--src/PursLoader/Glob.purs33
-rw-r--r--src/PursLoader/Loader.purs106
-rw-r--r--src/PursLoader/LoaderRef.purs74
-rw-r--r--src/PursLoader/LoaderUtil.purs13
-rw-r--r--src/PursLoader/Options.purs107
7 files changed, 456 insertions, 0 deletions
diff --git a/src/PursLoader/ChildProcess.purs b/src/PursLoader/ChildProcess.purs
new file mode 100644
index 0000000..34558fa
--- /dev/null
+++ b/src/PursLoader/ChildProcess.purs
@@ -0,0 +1,56 @@
1module PursLoader.ChildProcess
2 ( ChildProcess()
3 , spawn
4 ) where
5
6import Control.Monad.Aff (Aff(), makeAff)
7import Control.Monad.Eff (Eff())
8import Control.Monad.Eff.Exception (Error())
9
10import Data.Function
11
12foreign import data ChildProcess :: !
13
14spawn :: forall eff. String -> [String] -> Aff (cp :: ChildProcess | eff) String
15spawn command args = makeAff $ runFn4 spawnFn command args
16
17foreign import spawnFn """
18function spawnFn(command, args, errback, callback) {
19 return function(){
20 var child_process = require('child_process');
21
22 var process = child_process.spawn(command, args);
23
24 var stdout = new Buffer(0);
25
26 var stderr = new Buffer(0);
27
28 process.stdout.on('data', function(data){
29 stdout = Buffer.concat([stdout, new Buffer(data)]);
30 });
31
32 process.stderr.on('data', function(data){
33 stderr = Buffer.concat([stderr, new Buffer(data)]);
34 });
35
36 process.on('close', function(code){
37 var chalk = require('chalk');
38
39 var output = stdout.toString('utf-8');
40
41 var error = stderr.toString('utf-8');
42
43 if (error.length > 0) {
44 console.error('\n' + chalk.red('*') + ' ' + error);
45 }
46
47 if (code !== 0) errback(new Error('Process terminated with code ' + code))();
48 else callback(output)();
49 });
50 };
51}
52""" :: forall eff. Fn4 String
53 [String]
54 (Error -> Eff (cp :: ChildProcess | eff) Unit)
55 (String -> Eff (cp :: ChildProcess | eff) Unit)
56 (Eff (cp :: ChildProcess | eff) Unit)
diff --git a/src/PursLoader/FS.purs b/src/PursLoader/FS.purs
new file mode 100644
index 0000000..6955a63
--- /dev/null
+++ b/src/PursLoader/FS.purs
@@ -0,0 +1,67 @@
1module PursLoader.FS
2 ( FS()
3 , writeFileUtf8
4 , findFileUtf8
5 ) where
6
7import Control.Monad.Aff (Aff(), makeAff)
8import Control.Monad.Eff (Eff())
9import Control.Monad.Eff.Exception (Error())
10
11import Data.Maybe (Maybe(..))
12import Data.String.Regex (Regex())
13
14import Data.Function
15
16foreign import data FS :: !
17
18writeFileUtf8 :: forall eff. String -> String -> Aff (fs :: FS | eff) Unit
19writeFileUtf8 filepath contents = makeAff $ runFn4 writeFileUtf8Fn filepath contents
20
21foreign import writeFileUtf8Fn """
22function writeFileUtf8Fn(filepath, contents, errback, callback) {
23 return function(){
24 var fs = require('fs');
25
26 fs.writeFile(filepath, contents, function(error){
27 if (error) errback(error)();
28 else callback()();
29 });
30 };
31}
32""" :: forall eff. Fn4 String
33 String
34 (Error -> Eff (fs :: FS | eff) Unit)
35 (Unit -> Eff (fs :: FS | eff) Unit)
36 (Eff (fs :: FS | eff) Unit)
37
38findFileUtf8 :: forall eff. Regex -> [String] -> Aff (fs :: FS | eff) (Maybe String)
39findFileUtf8 regexp filepaths = makeAff $ runFn6 findFileUtf8Fn Nothing Just regexp filepaths
40
41foreign import findFileUtf8Fn """
42function findFileUtf8Fn(nothing, just, regex, filepaths, errback, callback) {
43 return function(){
44 var fs = require('fs');
45
46 var async = require('async');
47
48 function findFile(filepath, callback) {
49 fs.readFile(filepath, {encoding: 'utf-8'}, function(error, result){
50 if (error) callback(false);
51 else callback(regex.test(result));
52 });
53 }
54
55 async.detect(filepaths, findFile, function(result){
56 if (!result) callback(nothing)();
57 else callback(just(result))();
58 });
59 };
60}
61""" :: forall eff. Fn6 (Maybe String)
62 (String -> Maybe String)
63 Regex
64 [String]
65 (Error -> Eff (fs :: FS | eff) Unit)
66 (Maybe String -> Eff (fs :: FS | eff) Unit)
67 (Eff (fs :: FS | eff) Unit)
diff --git a/src/PursLoader/Glob.purs b/src/PursLoader/Glob.purs
new file mode 100644
index 0000000..392d9e4
--- /dev/null
+++ b/src/PursLoader/Glob.purs
@@ -0,0 +1,33 @@
1module PursLoader.Glob
2 ( Glob()
3 , globAll
4 ) where
5
6import Control.Monad.Aff (Aff(), makeAff)
7import Control.Monad.Eff (Eff())
8import Control.Monad.Eff.Exception (Error())
9
10import Data.Function
11
12foreign import data Glob :: !
13
14globAll :: forall eff. [String] -> Aff (glob :: Glob | eff) [[String]]
15globAll patterns = makeAff $ runFn3 globAllFn patterns
16
17foreign import globAllFn """
18function globAllFn(patterns, errback, callback) {
19 return function(){
20 var glob = require('glob');
21
22 var async = require('async');
23
24 async.map(patterns, glob, function(error, result){
25 if (error) errback(new Error(error))();
26 else callback(result)();
27 });
28 };
29}
30""" :: forall eff. Fn3 [String]
31 (Error -> Eff (glob :: Glob | eff) Unit)
32 ([[String]] -> Eff (glob :: Glob | eff) Unit)
33 (Eff (glob :: Glob | eff) Unit)
diff --git a/src/PursLoader/Loader.purs b/src/PursLoader/Loader.purs
new file mode 100644
index 0000000..e9e03c4
--- /dev/null
+++ b/src/PursLoader/Loader.purs
@@ -0,0 +1,106 @@
1module PursLoader.Loader
2 ( Effects()
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 ((!!), concat)
13import Data.Function (Fn2(), mkFn2)
14import Data.Maybe (Maybe(..), fromMaybe, maybe)
15import Data.String (joinWith)
16import Data.String.Regex (match, noFlags, regex, test)
17
18import PursLoader.ChildProcess (ChildProcess(), spawn)
19import PursLoader.FS (FS(), writeFileUtf8, findFileUtf8)
20import PursLoader.Glob (Glob(), globAll)
21import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, query, clearDependencies, addDependency, resourcePath)
22import PursLoader.LoaderUtil (parseQuery)
23import PursLoader.Options (loaderFFIOption, loaderSrcOption, pscOptions)
24
25type Effects eff = (cp :: ChildProcess, fs :: FS, glob :: Glob, loader :: Loader | eff)
26
27moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
28
29foreignRegex = regex "(?:^|\\n)\\s*foreign import\\s+" noFlags { ignoreCase = true }
30
31pscCommand = "psc"
32
33psciCommand = "psci"
34
35psciFilename = ".psci"
36
37(!!!) = flip (!!)
38
39foreign import cwd "var cwd = process.cwd();" :: String
40
41foreign import relative """
42function relative(from) {
43 return function(to){
44 var path = require('path');
45 return path.relative(from, to);
46 };
47}
48""" :: String -> String -> String
49
50mkPsci :: [[String]] -> [[String]] -> String
51mkPsci srcs ffis = joinWith "\n" ((loadModule <$> concat srcs) <> (loadForeign <$> concat ffis))
52 where
53 loadModule :: String -> String
54 loadModule a = ":m " ++ relative cwd a
55
56 loadForeign :: String -> String
57 loadForeign a = ":f " ++ relative cwd a
58
59findFFI :: forall eff. [[String]] -> String -> Aff (fs :: FS | eff) (Maybe String)
60findFFI ffiss name = findFileUtf8 re (concat ffiss)
61 where
62 re = regex ("(?:^|\\n)//\\s*module\\s*" ++ name ++ "\\s*\\n") noFlags
63
64loader' :: forall eff. LoaderRef -> String -> Aff (Effects eff) (Maybe String)
65loader' ref source = do
66 liftEff $ cacheable ref
67
68 let parsed = parseQuery $ query ref
69 srcs = fromMaybe [] (loaderSrcOption parsed)
70 ffis = fromMaybe [] (loaderFFIOption parsed)
71 opts = pscOptions parsed
72
73 spawn pscCommand (srcs <> opts)
74
75 srcss <- globAll srcs
76 ffiss <- globAll ffis
77
78 let psciFile = mkPsci srcss ffiss
79
80 writeFileUtf8 psciFilename psciFile
81
82 let moduleName = match moduleRegex source >>= (!!!) 1
83 hasForeign = test foreignRegex source
84 result = (\a -> "module.exports = require('" ++ a ++ "');") <$> moduleName
85
86 liftEff (clearDependencies ref)
87 liftEff (addDependency ref (resourcePath ref))
88
89 foreignPath <- if hasForeign
90 then fromMaybe (pure Nothing) (findFFI ffiss <$> moduleName)
91 else pure Nothing
92
93 fromMaybe (pure unit) ((\path -> liftEff (addDependency ref path)) <$> foreignPath)
94
95 return result
96
97loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit
98loader ref source = do
99 callback <- async ref
100 runAff (\e -> callback (Just e) "")
101 (maybe (callback (Just (error "Loader has failed to run")) "")
102 (callback Nothing))
103 (loader' ref source)
104
105loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit)
106loaderFn = mkFn2 loader
diff --git a/src/PursLoader/LoaderRef.purs b/src/PursLoader/LoaderRef.purs
new file mode 100644
index 0000000..f1efa04
--- /dev/null
+++ b/src/PursLoader/LoaderRef.purs
@@ -0,0 +1,74 @@
1module PursLoader.LoaderRef
2 ( LoaderRef()
3 , Loader()
4 , async
5 , cacheable
6 , query
7 , clearDependencies
8 , addDependency
9 , resourcePath
10 ) where
11
12import Control.Monad.Eff (Eff())
13import Control.Monad.Eff.Exception (Error())
14
15import Data.Function (Fn3(), runFn3)
16import Data.Maybe (Maybe(), fromMaybe, isJust)
17
18data LoaderRef
19
20foreign import data Loader :: !
21
22foreign import asyncFn """
23function asyncFn(isJust, fromMaybe, ref){
24 return function(){
25 var callback = ref.async();
26 return function(error){
27 return function(value){
28 return function(){
29 return isJust(error) ? callback(fromMaybe(new Error())(error))
30 : callback(null, value);
31 };
32 };
33 };
34 };
35}""" :: forall eff a. Fn3 (Maybe Error -> Boolean)
36 (Error -> Maybe Error -> Error)
37 LoaderRef
38 (Eff (loader :: Loader | eff) (Maybe Error -> a -> Eff (loader :: Loader | eff) Unit))
39
40async :: forall eff a. LoaderRef -> Eff (loader :: Loader | eff) (Maybe Error -> a -> Eff (loader :: Loader | eff) Unit)
41async ref = runFn3 asyncFn isJust fromMaybe ref
42
43foreign import cacheable """
44function cacheable(ref){
45 return function(){
46 return ref.cacheable && ref.cacheable();
47 };
48}""" :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
49
50foreign import query """
51function query(ref){
52 return ref.query;
53}""" :: LoaderRef -> String
54
55foreign import clearDependencies """
56function clearDependencies(ref){
57 return function(){
58 return ref.clearDependencies();
59 };
60}""" :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
61
62foreign import resourcePath """
63function resourcePath(ref){
64 return ref.resourcePath;
65}""" :: LoaderRef -> String
66
67foreign import addDependency """
68function addDependency(ref){
69 return function(dep){
70 return function(){
71 return ref.addDependency(dep);
72 };
73 };
74}""" :: forall eff. LoaderRef -> String -> Eff (loader :: Loader | eff) Unit
diff --git a/src/PursLoader/LoaderUtil.purs b/src/PursLoader/LoaderUtil.purs
new file mode 100644
index 0000000..86be124
--- /dev/null
+++ b/src/PursLoader/LoaderUtil.purs
@@ -0,0 +1,13 @@
1module PursLoader.LoaderUtil
2 ( parseQuery
3 ) where
4
5import Data.Foreign (Foreign())
6
7import PursLoader.LoaderRef (LoaderRef())
8
9foreign import parseQuery """
10function parseQuery(query){
11 var loaderUtils = require('loader-utils');
12 return loaderUtils.parseQuery(query);
13}""" :: String -> Foreign
diff --git a/src/PursLoader/Options.purs b/src/PursLoader/Options.purs
new file mode 100644
index 0000000..51e9be5
--- /dev/null
+++ b/src/PursLoader/Options.purs
@@ -0,0 +1,107 @@
1module PursLoader.Options
2 ( pscOptions
3 , loaderSrcOption
4 , loaderFFIOption
5 ) where
6
7import Data.Array (concat)
8import Data.Either (either)
9
10import Data.Foreign (Foreign(), F())
11import Data.Foreign.Class (IsForeign, read, readProp)
12import Data.Foreign.NullOrUndefined (NullOrUndefined(..), runNullOrUndefined)
13
14import Data.Maybe (Maybe(..), maybe, fromMaybe)
15
16noPreludeOpt = "no-prelude"
17
18noOptsOpt = "no-opts"
19
20noMagicDoOpt = "no-magic-do"
21
22noTcoOpt = "no-tco"
23
24verboseErrorsOpt = "verbose-errors"
25
26outputOpt = "output"
27
28commentsOpt = "comments"
29
30noPrefixOpt = "no-prefix"
31
32requirePathOpt = "require-path"
33
34srcOpt = "src"
35
36ffiOpt = "ffi"
37
38newtype Options
39 = Options { noPrelude :: NullOrUndefined Boolean
40 , noOpts :: NullOrUndefined Boolean
41 , noMagicDo :: NullOrUndefined Boolean
42 , noTco :: NullOrUndefined Boolean
43 , verboseErrors :: NullOrUndefined Boolean
44 , comments :: NullOrUndefined Boolean
45 , output :: NullOrUndefined String
46 , noPrefix :: NullOrUndefined Boolean
47 , requirePath :: NullOrUndefined String
48 , src :: NullOrUndefined [String]
49 , ffi :: NullOrUndefined [String]
50 }
51
52instance isForeignOptions :: IsForeign Options where
53 read obj = Options <$> ({ noPrelude: _
54 , noOpts: _
55 , noMagicDo: _
56 , noTco: _
57 , verboseErrors: _
58 , comments: _
59 , output: _
60 , noPrefix: _
61 , requirePath: _
62 , src: _
63 , ffi: _
64 } <$> readProp noPreludeOpt obj
65 <*> readProp noOptsOpt obj
66 <*> readProp noMagicDoOpt obj
67 <*> readProp noTcoOpt obj
68 <*> readProp verboseErrorsOpt obj
69 <*> readProp commentsOpt obj
70 <*> readProp outputOpt obj
71 <*> readProp noPrefixOpt obj
72 <*> readProp requirePathOpt obj
73 <*> readProp srcOpt obj
74 <*> readProp ffiOpt obj)
75
76class LoaderOption a where
77 opt :: String -> NullOrUndefined a -> [String]
78
79instance booleanLoaderOption :: LoaderOption Boolean where
80 opt key val = maybe [] (\a -> if a then ["--" ++ key] else []) (runNullOrUndefined val)
81
82instance stringLoaderOption :: LoaderOption String where
83 opt key val = maybe [] (\a -> ["--" ++ key ++ "=" ++ a]) (runNullOrUndefined val)
84
85instance arrayLoaderOption :: (LoaderOption a) => LoaderOption [a] where
86 opt key val = concat (opt key <$> (NullOrUndefined <<< Just)
87 <$> (fromMaybe [] (runNullOrUndefined val)))
88
89pscOptions :: Foreign -> [String]
90pscOptions query = either (const []) fold parsed
91 where parsed = read query :: F Options
92 fold (Options a) = opt noPreludeOpt a.noPrelude <>
93 opt noOptsOpt a.noOpts <>
94 opt noMagicDoOpt a.noMagicDo <>
95 opt noTcoOpt a.noTco <>
96 opt verboseErrorsOpt a.verboseErrors <>
97 opt commentsOpt a.comments <>
98 opt outputOpt a.output <>
99 opt noPrefixOpt a.noPrefix <>
100 opt requirePathOpt a.requirePath <>
101 opt ffiOpt a.ffi
102
103loaderSrcOption :: Foreign -> Maybe [String]
104loaderSrcOption query = either (const Nothing) (\(Options a) -> runNullOrUndefined a.src) (read query)
105
106loaderFFIOption :: Foreign -> Maybe [String]
107loaderFFIOption query = either (const Nothing) (\(Options a) -> runNullOrUndefined a.ffi) (read query)