aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--.npmignore1
-rw-r--r--LICENSE2
-rw-r--r--MODULE.md226
-rw-r--r--README.md2
-rw-r--r--bower.json16
-rw-r--r--entry.js11
-rw-r--r--example/bower.json8
-rw-r--r--example/package.json6
-rw-r--r--example/src/entry.js6
-rw-r--r--example/webpack.config.js30
-rw-r--r--gulpfile.js68
-rw-r--r--index.js62
-rw-r--r--package.json39
-rw-r--r--src/ChildProcess.purs40
-rw-r--r--src/FS.purs45
-rw-r--r--src/Glob.purs31
-rw-r--r--src/Loader.purs114
-rw-r--r--src/LoaderRef.purs75
-rw-r--r--src/LoaderUtil.purs20
-rw-r--r--src/OS.purs3
-rw-r--r--src/Options.purs72
-rw-r--r--src/Path.purs36
-rw-r--r--webpack.config.js29
24 files changed, 840 insertions, 109 deletions
diff --git a/.gitignore b/.gitignore
index 8cde684..dd8eead 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,11 @@
1.psci
1npm-debug.log 2npm-debug.log
3index.json
4index.js
2node_modules/ 5node_modules/
6bower_components/
7build/
8example/bundle.js
3example/node_modules/ 9example/node_modules/
4example/bower_components/ 10example/bower_components/
5example/dist/
6example/output/ 11example/output/
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index 90c978b..0000000
--- a/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
1example/
diff --git a/LICENSE b/LICENSE
index aaed7de..05b0016 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
1Copyright (c) 2014 Eric Thul 1Copyright (c) 2015 Eric Thul
2 2
3Permission is hereby granted, free of charge, to any person obtaining a 3Permission is hereby granted, free of charge, to any person obtaining a
4copy of this software and associated documentation files (the 4copy of this software and associated documentation files (the
diff --git a/MODULE.md b/MODULE.md
new file mode 100644
index 0000000..39d9d3a
--- /dev/null
+++ b/MODULE.md
@@ -0,0 +1,226 @@
1# Module Documentation
2
3## Module PursLoader.ChildProcess
4
5#### `ChildProcess`
6
7``` purescript
8data ChildProcess :: !
9```
10
11
12#### `spawn`
13
14``` purescript
15spawn :: forall eff. String -> [String] -> Aff (cp :: ChildProcess | eff) String
16```
17
18
19
20## Module PursLoader.FS
21
22#### `FS`
23
24``` purescript
25data FS :: !
26```
27
28
29#### `readFileUtf8`
30
31``` purescript
32readFileUtf8 :: forall eff. String -> Aff (fs :: FS | eff) String
33```
34
35
36#### `readFileUtf8Sync`
37
38``` purescript
39readFileUtf8Sync :: forall eff. String -> Eff (fs :: FS | eff) String
40```
41
42
43
44## Module PursLoader.Glob
45
46#### `Glob`
47
48``` purescript
49data Glob :: !
50```
51
52
53#### `glob`
54
55``` purescript
56glob :: forall eff. String -> Aff (glob :: Glob | eff) [String]
57```
58
59
60
61## Module PursLoader.Loader
62
63#### `LoaderEff`
64
65``` purescript
66type LoaderEff eff a = Eff (fs :: FS, cp :: ChildProcess, glob :: Glob, loader :: Loader | eff) a
67```
68
69
70#### `loader`
71
72``` purescript
73loader :: forall eff. LoaderRef -> String -> LoaderEff eff Unit
74```
75
76
77#### `loaderFn`
78
79``` purescript
80loaderFn :: forall eff. Fn2 LoaderRef String (LoaderEff eff Unit)
81```
82
83
84
85## Module PursLoader.LoaderRef
86
87#### `LoaderRef`
88
89``` purescript
90data LoaderRef
91```
92
93
94#### `Loader`
95
96``` purescript
97data Loader :: !
98```
99
100
101#### `async`
102
103``` purescript
104async :: forall eff a. LoaderRef -> Eff (loader :: Loader | eff) (Maybe Error -> a -> Eff (loader :: Loader | eff) Unit)
105```
106
107
108#### `cacheable`
109
110``` purescript
111cacheable :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
112```
113
114
115#### `clearDependencies`
116
117``` purescript
118clearDependencies :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
119```
120
121
122#### `resourcePath`
123
124``` purescript
125resourcePath :: LoaderRef -> String
126```
127
128
129#### `addDependency`
130
131``` purescript
132addDependency :: forall eff. LoaderRef -> String -> Eff (loader :: Loader | eff) Unit
133```
134
135
136#### `query`
137
138``` purescript
139query :: LoaderRef -> String
140```
141
142
143
144## Module PursLoader.LoaderUtil
145
146#### `getRemainingRequest`
147
148``` purescript
149getRemainingRequest :: LoaderRef -> String
150```
151
152
153#### `parseQuery`
154
155``` purescript
156parseQuery :: String -> Foreign
157```
158
159
160
161## Module PursLoader.OS
162
163#### `eol`
164
165``` purescript
166eol :: String
167```
168
169
170
171## Module PursLoader.Options
172
173#### `isForeignOptions`
174
175``` purescript
176instance isForeignOptions :: IsForeign Options
177```
178
179
180#### `pscMakeOutputOption`
181
182``` purescript
183pscMakeOutputOption :: Foreign -> Maybe String
184```
185
186
187#### `pscMakeOptions`
188
189``` purescript
190pscMakeOptions :: Foreign -> [String]
191```
192
193
194
195## Module PursLoader.Path
196
197#### `dirname`
198
199``` purescript
200dirname :: String -> String
201```
202
203
204#### `join`
205
206``` purescript
207join :: [String] -> String
208```
209
210
211#### `relative`
212
213``` purescript
214relative :: String -> String -> String
215```
216
217
218#### `resolve`
219
220``` purescript
221resolve :: String -> String
222```
223
224
225
226
diff --git a/README.md b/README.md
index dcbedf2..ee96448 100644
--- a/README.md
+++ b/README.md
@@ -27,4 +27,4 @@ npm install purs-loader --save-dev
27 27
28## Example 28## Example
29 29
30See the [example](https://github.com/ethul/purs-loader/tree/topic/bower-components/example) directory for a complete example. 30See the [example](https://github.com/ethul/purs-loader/tree/master/example) directory for a complete example.
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..dddddf9
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,16 @@
1{
2 "name": "purs-loader",
3 "private": true,
4 "devDependencies": {
5 "purescript-aff": "~0.9.1",
6 "purescript-exceptions": "~0.2.3",
7 "purescript-strings": "~0.4.5",
8 "purescript-maybe": "~0.2.2",
9 "purescript-foreign": "~0.4.2",
10 "purescript-foldable-traversable": "~0.3.1",
11 "purescript-tuples": "~0.3.4",
12 "purescript-maps": "~0.3.3",
13 "purescript-arrays": "~0.3.7",
14 "purescript-monad-eff": "~0.1.0"
15 }
16}
diff --git a/entry.js b/entry.js
new file mode 100644
index 0000000..87f52d3
--- /dev/null
+++ b/entry.js
@@ -0,0 +1,11 @@
1'use strict';
2
3var pursLoader = require('PursLoader.Loader');
4
5function loader(source) {
6 var ref = this;
7 var result = pursLoader.loaderFn(ref, source);
8 return result();
9}
10
11module.exports = loader;
diff --git a/example/bower.json b/example/bower.json
index 8d71688..b4a1c78 100644
--- a/example/bower.json
+++ b/example/bower.json
@@ -1,14 +1,6 @@
1{ 1{
2 "name": "example", 2 "name": "example",
3 "license": "MIT",
4 "private": true, 3 "private": true,
5 "ignore": [
6 "**/.*",
7 "node_modules",
8 "bower_components",
9 "test",
10 "tests"
11 ],
12 "devDependencies": { 4 "devDependencies": {
13 "purescript-maybe": "~0.2.1" 5 "purescript-maybe": "~0.2.1"
14 } 6 }
diff --git a/example/package.json b/example/package.json
index 6b0c04c..915eb27 100644
--- a/example/package.json
+++ b/example/package.json
@@ -4,12 +4,12 @@
4 "private": true, 4 "private": true,
5 "scripts": { 5 "scripts": {
6 "webpack": "./node_modules/.bin/webpack", 6 "webpack": "./node_modules/.bin/webpack",
7 "run": "node dist/app.js", 7 "run": "node bundle.js",
8 "clean": "rm -rf bower_components && rm -rf dist && rm -rf node_modules && rm -rf output" 8 "clean": "rm -rf bower_components && rm -rf bundle.js && rm -rf node_modules && rm -rf output"
9 }, 9 },
10 "license": "MIT", 10 "license": "MIT",
11 "devDependencies": { 11 "devDependencies": {
12 "purs-loader": "file:../", 12 "purs-loader": "file:../",
13 "webpack": "^1.4.15" 13 "webpack": "^1.8.4"
14 } 14 }
15} 15}
diff --git a/example/src/entry.js b/example/src/entry.js
index 160bee4..cc09034 100644
--- a/example/src/entry.js
+++ b/example/src/entry.js
@@ -1,7 +1,7 @@
1var test = require('purs?output=output!./Test.purs'); 1var test = require('./Test');
2 2
3var foo = require('purs?output=output!./Foo.purs'); 3var foo = require('./Foo');
4 4
5var baz = require('purs?output=output!./Foo/Baz.purs'); 5var baz = require('./Foo/Baz');
6 6
7console.log(test, foo, baz); 7console.log(test, foo, baz);
diff --git a/example/webpack.config.js b/example/webpack.config.js
index 629138a..19997f3 100644
--- a/example/webpack.config.js
+++ b/example/webpack.config.js
@@ -1,16 +1,18 @@
1var path = require('path'); 1var path = require('path');
2 2
3module.exports = { 3var config
4 entry: './src/entry', 4 = { entry: './src/entry'
5 output: { 5 , output: { path: __dirname
6 path: path.join(__dirname, 'dist'), 6 , filename: 'bundle.js'
7 filename: 'app.js' 7 }
8 }, 8 , module: { loaders: [ { test: /\.purs$/, loader: 'purs-loader' } ] }
9 resolve: { 9 , resolve: { modulesDirectories: [ 'node_modules',
10 modulesDirectories: [ 10 'output'
11 'node_modules', 11 ]
12 'web_modules', 12 , extensions: ['', '.js', '.purs']
13 'output' 13 }
14 ] 14 , resolveLoader: { root: path.join(__dirname, 'node_modules') }
15 } 15 }
16}; 16 ;
17
18module.exports = config;
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..e217480
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,68 @@
1'use strict';
2
3var path = require('path');
4
5var gulp = require('gulp');
6
7var gutil = require('gulp-util');
8
9var plumber = require('gulp-plumber');
10
11var purescript = require('gulp-purescript');
12
13var sequence = require('run-sequence');
14
15var del = require('del');
16
17var config = { del: ['build', 'index.js']
18 , purescript: { src: [ 'bower_components/purescript-*/src/**/*.purs*'
19 , 'src/**/*.purs'
20 ]
21 , dest: 'build'
22 , docs: 'MODULE.md'
23 }
24 }
25 ;
26
27function error(e) {
28 gutil.log(gutil.colors.magenta('>>>> Error <<<<') + '\n' + e.toString().trim());
29 this.emit('end');
30}
31
32gulp.task('del', function(cb){
33 del(config.del, cb);
34});
35
36gulp.task('make', function(){
37 return gulp.src(config.purescript.src).
38 pipe(plumber()).
39 pipe(purescript.pscMake({output: config.purescript.dest})).
40 on('error', error);
41});
42
43gulp.task('psci', function(){
44 return gulp.src(config.purescript.src).
45 pipe(plumber()).
46 pipe(purescript.dotPsci()).
47 on('error', error);
48});
49
50gulp.task('docs', function(){
51 return gulp.src(config.purescript.src[1]).
52 pipe(plumber()).
53 pipe(purescript.pscDocs()).
54 on('error', error).
55 pipe(gulp.dest(config.purescript.docs));
56});
57
58gulp.task('watch', function(){
59 gulp.watch(config.purescript.src, ['make']);
60});
61
62gulp.task('default', function(callback){
63 sequence('del', 'make', ['psci', 'docs'], callback);
64});
65
66gulp.task('build', function(callback){
67 sequence('del', 'make', callback);
68});
diff --git a/index.js b/index.js
deleted file mode 100644
index 4a74d15..0000000
--- a/index.js
+++ /dev/null
@@ -1,62 +0,0 @@
1var cp = require('child_process')
2 , path = require('path')
3 , fs = require('fs')
4 , glob = require('glob')
5 , lodash = require('lodash')
6 , chalk = require('chalk')
7 , lu = require('loader-utils')
8 , cwd = process.cwd()
9 , MODULE_RE = /^module\s+([\w\.]+)\s+/i
10 , BOWER_PATTERN = path.join('bower_components', 'purescript-*', 'src')
11 , PSC_MAKE = 'psc-make'
12 , OUTPUT = 'output'
13 , OPTIONS = {
14 'no-prelude': '--no-prelude',
15 'no-opts': '--no-opts',
16 'no-magic-do': '--no-magic-do',
17 'no-tco': '--no-tco',
18 'verbose-errors': '--verbose-errors',
19 'output': '--output'
20 }
21;
22
23function pattern(root) {
24 var as = [ BOWER_PATTERN, root ];
25 return path.join('{' + as.join(',') + '}', '**', '*.purs');
26}
27
28module.exports = function(source){
29 var callback = this.async()
30 , request = lu.getRemainingRequest(this)
31 , root = path.dirname(path.relative(cwd, request))
32 , query = lu.parseQuery(this.query)
33 , opts = lodash.foldl(lodash.keys(query), function(acc, k){
34 var h = function(v){return acc.concat(query[k] && OPTIONS[k] ? [v] : []);}
35 if (k === OUTPUT) return h(OPTIONS[k] + '=' + query[k]);
36 else return h(OPTIONS[k]);
37 }, [])
38 ;
39 glob(pattern(root), function(e, files){
40 if (e !== null) callback(e);
41 else {
42 var cmd = cp.spawn(PSC_MAKE, opts.concat(files));
43 cmd.on('close', function(e){
44 if (e) callback(e);
45 else {
46 var result = MODULE_RE.exec(source);
47 var module = result.length > 1 ? result[1] : '';
48 fs.readFile(path.join(query[OUTPUT] || OUTPUT, module, 'index.js'), 'utf-8', function(e, output){
49 if (e) callback(e);
50 else callback(e, output);
51 });
52 }
53 });
54 cmd.stdout.on('data', function(stdout){
55 console.log('Stdout from \'' + chalk.cyan(PSC_MAKE) + '\'\n' + chalk.magenta(stdout));
56 });
57 cmd.stderr.on('data', function(stderr){
58 console.log('Stderr from \'' + chalk.cyan(PSC_MAKE) + '\'\n' + chalk.magenta(stderr));
59 });
60 }
61 });
62};
diff --git a/package.json b/package.json
index 4d458b8..d7e986f 100644
--- a/package.json
+++ b/package.json
@@ -2,24 +2,33 @@
2 "name": "purs-loader", 2 "name": "purs-loader",
3 "version": "0.0.3", 3 "version": "0.0.3",
4 "description": "PureScript loader for webpack", 4 "description": "PureScript loader for webpack",
5 "main": "index.js", 5 "license": "MIT",
6 "scripts": { 6 "repository": "ethul/purs-loader",
7 "test": "echo \"Error: no test specified\" && exit 1" 7 "author": {
8 "name": "Eric Thul",
9 "email": "thul.eric@gmail.com"
8 }, 10 },
9 "repository": { 11 "scripts": {
10 "type": "git", 12 "build": "npm run-script build:compile && npm run-script build:package",
11 "url": "git@github.com:ethul/purs-loader.git" 13 "build:compile": "gulp build",
14 "build:package": "./node_modules/.bin/webpack --progress --colors --profile --bail",
15 "build:json": "./node_modules/.bin/webpack --progress --colors --profile --bail --json > index.json",
16 "prepublish": "npm run-script build"
12 }, 17 },
13 "author": "Eric Thul", 18 "files": [
14 "license": "MIT", 19 "index.js"
15 "bugs": { 20 ],
16 "url": "https://github.com/ethul/purs-loader/issues" 21 "devDependencies": {
22 "del": "^1.1.1",
23 "gulp": "^3.8.11",
24 "gulp-plumber": "^1.0.0",
25 "gulp-purescript": "^0.3.1",
26 "gulp-util": "^3.0.4",
27 "run-sequence": "^1.0.2",
28 "webpack": "^1.8.4"
17 }, 29 },
18 "homepage": "https://github.com/ethul/purs-loader",
19 "dependencies": { 30 "dependencies": {
20 "chalk": "^0.5.1", 31 "glob": "^5.0.3",
21 "glob": "4.0.6", 32 "loader-utils": "^0.2.6"
22 "loader-utils": "^0.2.3",
23 "lodash": "^2.4.1"
24 } 33 }
25} 34}
diff --git a/src/ChildProcess.purs b/src/ChildProcess.purs
new file mode 100644
index 0000000..c9ff23b
--- /dev/null
+++ b/src/ChildProcess.purs
@@ -0,0 +1,40 @@
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 process.stdout.on('data', function(data){
27 stdout = Buffer.concat([stdout, new Buffer(data)]);
28 });
29
30 process.on('close', function(code){
31 if (code !== 0) errback(new Error(stdout.toString()))();
32 else callback(stdout.toString())();
33 });
34 };
35}
36""" :: forall eff. Fn4 String
37 [String]
38 (Error -> Eff (cp :: ChildProcess | eff) Unit)
39 (String -> Eff (cp :: ChildProcess | eff) Unit)
40 (Eff (cp :: ChildProcess | eff) Unit)
diff --git a/src/FS.purs b/src/FS.purs
new file mode 100644
index 0000000..68fe2f9
--- /dev/null
+++ b/src/FS.purs
@@ -0,0 +1,45 @@
1module PursLoader.FS
2 ( FS()
3 , readFileUtf8
4 , readFileUtf8Sync
5 ) where
6
7import Control.Monad.Aff (Aff(), makeAff)
8import Control.Monad.Eff (Eff())
9import Control.Monad.Eff.Exception (Error())
10
11import Data.Function
12
13foreign import data FS :: !
14
15readFileUtf8 :: forall eff. String -> Aff (fs :: FS | eff) String
16readFileUtf8 filepath = makeAff $ runFn3 readFileUtf8Fn filepath
17
18readFileUtf8Sync :: forall eff. String -> Eff (fs :: FS | eff) String
19readFileUtf8Sync filepath = readFileUtf8SyncFn filepath
20
21foreign import readFileUtf8Fn """
22function readFileUtf8Fn(filepath, errback, callback) {
23 return function(){
24 var fs = require('fs');
25
26 fs.readFile(filepath, 'utf-8', function(e, data){
27 if (e) errback(e)();
28 else callback(data)();
29 });
30 };
31}
32""" :: forall eff. Fn3 String
33 (Error -> Eff (fs :: FS | eff) Unit)
34 (String -> Eff (fs :: FS | eff) Unit)
35 (Eff (fs :: FS | eff) Unit)
36
37foreign import readFileUtf8SyncFn """
38function readFileUtf8SyncFn(filepath) {
39 return function(){
40 var fs = require('fs');
41
42 return fs.readFileSync(filepath, {encoding: 'utf-8'});
43 };
44}
45""" :: forall eff. String -> (Eff (fs :: FS | eff) String)
diff --git a/src/Glob.purs b/src/Glob.purs
new file mode 100644
index 0000000..7bc9212
--- /dev/null
+++ b/src/Glob.purs
@@ -0,0 +1,31 @@
1module PursLoader.Glob
2 ( Glob()
3 , glob
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
14glob :: forall eff. String -> Aff (glob :: Glob | eff) [String]
15glob pattern = makeAff $ runFn3 globFn pattern
16
17foreign import globFn """
18function globFn(pattern, errback, callback) {
19 return function(){
20 var glob = require('glob');
21
22 glob(pattern, function(e, data){
23 if (e) errback(e)();
24 else callback(data)();
25 });
26 };
27}
28""" :: forall eff. Fn3 String
29 (Error -> Eff (glob :: Glob | eff) Unit)
30 ([String] -> Eff (glob :: Glob | eff) Unit)
31 (Eff (glob :: Glob | eff) Unit)
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 @@
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
12import Data.Array ((!!), catMaybes, concat, nub, null)
13import Data.Function (Fn2(), mkFn2)
14import Data.Maybe (Maybe(..), fromMaybe, maybe)
15import Data.String (joinWith, split)
16import Data.String.Regex (Regex(), match, noFlags, regex)
17import Data.StrMap (StrMap(), fromList, lookup)
18import Data.Traversable (sequence)
19import Data.Tuple.Nested (tuple2)
20
21import PursLoader.ChildProcess (ChildProcess(), spawn)
22import PursLoader.FS (FS(), readFileUtf8, readFileUtf8Sync)
23import PursLoader.Glob (Glob(), glob)
24import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, clearDependencies, addDependency, query, resourcePath)
25import PursLoader.LoaderUtil (getRemainingRequest, parseQuery)
26import PursLoader.OS (eol)
27import PursLoader.Options (pscMakeOptions, pscMakeDefaultOutput, pscMakeOutputOption)
28import PursLoader.Path (dirname, join, relative, resolve)
29
30foreign import cwd "var cwd = process.cwd();" :: String
31
32moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
33
34importRegex = regex "^\\s*import\\s+(?:qualified\\s+)?([\\w\\.]+)" noFlags { ignoreCase = true }
35
36bowerPattern = join [ "bower_components", "purescript-*", "src" ]
37
38pscMakeCommand = "psc-make"
39
40indexFilename = "index.js"
41
42(!!!) = flip (!!)
43
44pursPattern :: String -> String
45pursPattern root = join [ "{" ++ joinWith "," [ bowerPattern, root ] ++ "}"
46 , "**"
47 , "*.purs"
48 ]
49
50type GraphModule = { file :: String, imports :: [String] }
51
52type Graph = StrMap GraphModule
53
54mkGraph :: forall eff. [String] -> Eff (fs :: FS | eff) Graph
55mkGraph 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
62mkDeps :: forall eff. String -> Graph -> [String]
63mkDeps 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
69addDeps :: forall eff. LoaderRef -> Graph -> [String] -> Eff (loader :: Loader | eff) Unit
70addDeps 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
75type LoaderAff eff a = Aff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
76
77loader' :: forall eff. LoaderRef -> String -> LoaderAff eff (Maybe String)
78loader' 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
103type LoaderEff eff a = Eff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
104
105loader :: forall eff. LoaderRef -> String -> LoaderEff eff Unit
106loader 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
113loaderFn :: forall eff. Fn2 LoaderRef String (LoaderEff eff Unit)
114loaderFn = mkFn2 loader
diff --git a/src/LoaderRef.purs b/src/LoaderRef.purs
new file mode 100644
index 0000000..2d62754
--- /dev/null
+++ b/src/LoaderRef.purs
@@ -0,0 +1,75 @@
1module PursLoader.LoaderRef
2 ( LoaderRef()
3 , Loader()
4 , async
5 , cacheable
6 , clearDependencies
7 , resourcePath
8 , addDependency
9 , query
10 ) where
11
12import Control.Monad.Eff (Eff())
13import Control.Monad.Eff.Exception (Error())
14
15import Data.Foreign (Foreign())
16import Data.Function (Fn3(), runFn3)
17import Data.Maybe (Maybe(), fromMaybe, isJust)
18
19data LoaderRef
20
21foreign import data Loader :: !
22
23foreign import asyncFn """
24function asyncFn(isJust, fromMaybe, ref){
25 return function(){
26 var callback = ref.async();
27 return function(error){
28 return function(value){
29 return function(){
30 return isJust(error) ? callback(fromMaybe(new Error())(error))
31 : callback(null, value);
32 };
33 };
34 };
35 };
36}""" :: forall eff a. Fn3 (Maybe Error -> Boolean)
37 (Error -> Maybe Error -> Error)
38 LoaderRef
39 (Eff (loader :: Loader | eff) (Maybe Error -> a -> Eff (loader :: Loader | eff) Unit))
40
41async :: forall eff a. LoaderRef -> Eff (loader :: Loader | eff) (Maybe Error -> a -> Eff (loader :: Loader | eff) Unit)
42async ref = runFn3 asyncFn isJust fromMaybe ref
43
44foreign import cacheable """
45function cacheable(ref){
46 return function(){
47 return ref.cacheable && ref.cacheable();
48 };
49}""" :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
50
51foreign import clearDependencies """
52function clearDependencies(ref){
53 return function(){
54 return ref.clearDependencies();
55 };
56}""" :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
57
58foreign import resourcePath """
59function resourcePath(ref){
60 return ref.resourcePath;
61}""" :: LoaderRef -> String
62
63foreign import addDependency """
64function addDependency(ref){
65 return function(dep){
66 return function(){
67 return ref.addDependency(dep);
68 };
69 };
70}""" :: forall eff. LoaderRef -> String -> Eff (loader :: Loader | eff) Unit
71
72foreign import query """
73function query(ref){
74 return ref.query;
75}""" :: LoaderRef -> String
diff --git a/src/LoaderUtil.purs b/src/LoaderUtil.purs
new file mode 100644
index 0000000..f22be44
--- /dev/null
+++ b/src/LoaderUtil.purs
@@ -0,0 +1,20 @@
1module PursLoader.LoaderUtil
2 ( getRemainingRequest
3 , parseQuery
4 ) where
5
6import Data.Foreign (Foreign())
7
8import PursLoader.LoaderRef (LoaderRef())
9
10foreign import getRemainingRequest """
11function getRemainingRequest(ref){
12 var loaderUtils = require('loader-utils');
13 return loaderUtils.getRemainingRequest(ref);
14}""" :: LoaderRef -> String
15
16foreign import parseQuery """
17function parseQuery(query){
18 var loaderUtils = require('loader-utils');
19 return loaderUtils.parseQuery(query);
20}""" :: String -> Foreign
diff --git a/src/OS.purs b/src/OS.purs
new file mode 100644
index 0000000..590c3d6
--- /dev/null
+++ b/src/OS.purs
@@ -0,0 +1,3 @@
1module PursLoader.OS (eol) where
2
3foreign import eol "var eol = require('os').EOL;" :: String
diff --git a/src/Options.purs b/src/Options.purs
new file mode 100644
index 0000000..b96cddc
--- /dev/null
+++ b/src/Options.purs
@@ -0,0 +1,72 @@
1module PursLoader.Options
2 ( pscMakeOptions
3 , pscMakeDefaultOutput
4 , pscMakeOutputOption
5 ) where
6
7import Data.Either (either)
8
9import Data.Foreign (Foreign(), F())
10import Data.Foreign.Class (IsForeign, read, readProp)
11import Data.Foreign.NullOrUndefined (NullOrUndefined(), runNullOrUndefined)
12
13import Data.Maybe (Maybe(..), maybe)
14
15noPreludeOpt = "no-prelude"
16
17noOptsOpt = "no-opts"
18
19noMagicDoOpt = "no-magic-do"
20
21noTcoOpt = "no-tco"
22
23verboseErrorsOpt = "verbose-errors"
24
25outputOpt = "output"
26
27pscMakeDefaultOutput = "output"
28
29newtype Options
30 = Options { noPrelude :: NullOrUndefined Boolean
31 , noOpts :: NullOrUndefined Boolean
32 , noMagicDo :: NullOrUndefined Boolean
33 , noTco :: NullOrUndefined Boolean
34 , verboseErrors :: NullOrUndefined Boolean
35 , output :: NullOrUndefined String
36 }
37
38instance isForeignOptions :: IsForeign Options where
39 read obj = (\a b c d e f ->
40 Options { noPrelude: a
41 , noOpts: b
42 , noMagicDo: c
43 , noTco: d
44 , verboseErrors: e
45 , output: f
46 }) <$> readProp noPreludeOpt obj
47 <*> readProp noOptsOpt obj
48 <*> readProp noMagicDoOpt obj
49 <*> readProp noTcoOpt obj
50 <*> readProp verboseErrorsOpt obj
51 <*> readProp outputOpt obj
52
53booleanOpt :: String -> NullOrUndefined Boolean -> [String]
54booleanOpt key opt = maybe [] (\a -> if a then ["--" ++ key] else []) (runNullOrUndefined opt)
55
56stringOpt :: String -> NullOrUndefined String -> [String]
57stringOpt key opt = maybe [] (\a -> ["--" ++ key ++ "=" ++ a]) (runNullOrUndefined opt)
58
59pscMakeOutputOption :: Foreign -> Maybe String
60pscMakeOutputOption query = either (const Nothing)
61 (\(Options a) -> runNullOrUndefined a.output)
62 (read query)
63
64pscMakeOptions :: Foreign -> [String]
65pscMakeOptions query = either (const []) fold parsed
66 where parsed = read query :: F Options
67 fold (Options a) = booleanOpt noPreludeOpt a.noPrelude <>
68 booleanOpt noOptsOpt a.noOpts <>
69 booleanOpt noMagicDoOpt a.noMagicDo <>
70 booleanOpt noTcoOpt a.noTco <>
71 booleanOpt verboseErrorsOpt a.verboseErrors <>
72 stringOpt outputOpt a.output
diff --git a/src/Path.purs b/src/Path.purs
new file mode 100644
index 0000000..e071e35
--- /dev/null
+++ b/src/Path.purs
@@ -0,0 +1,36 @@
1module PursLoader.Path
2 ( dirname
3 , join
4 , relative
5 , resolve
6 ) where
7
8foreign import dirname """
9function dirname(filepath) {
10 var path = require('path');
11 return path.dirname(filepath);
12}
13""" :: String -> String
14
15foreign import join """
16function join(parts) {
17 var path = require('path');
18 return path.join.apply(path, parts);
19}
20""" :: [String] -> String
21
22foreign import relative """
23function relative(from) {
24 return function(to){
25 var path = require('path');
26 return path.relative(from, to);
27 };
28}
29""" :: String -> String -> String
30
31foreign import resolve """
32function resolve(filepath) {
33 var path = require('path');
34 return path.resolve(filepath);
35}
36""" :: String -> String
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..11b9069
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,29 @@
1'use strict';
2
3var path = require('path');
4
5var webpack = require('webpack');
6
7var noErrorsPlugin = webpack.NoErrorsPlugin;
8
9var dedupePlugin = webpack.optimize.DedupePlugin;
10
11var config
12 = { cache: true
13 , target: 'node'
14 , entry: { index: './entry' }
15 , externals: { 'glob': 'commonjs glob'
16 , 'loader-utils': 'commonjs loader-utils'
17 }
18 , output: { path: __dirname
19 , filename: '[name].js'
20 , libraryTarget: 'commonjs2'
21 }
22 , plugins: [ new noErrorsPlugin()
23 , new dedupePlugin()
24 ]
25 , resolve: { modulesDirectories: [ 'build' ] }
26 }
27 ;
28
29module.exports = config;