diff options
-rw-r--r-- | .babelrc | 1 | ||||
-rw-r--r-- | .gitignore | 9 | ||||
-rw-r--r-- | LICENSE | 2 | ||||
-rw-r--r-- | README.md | 60 | ||||
-rw-r--r-- | bower.json | 10 | ||||
-rw-r--r-- | docs/PursLoader/Debug.md | 9 | ||||
-rw-r--r-- | docs/PursLoader/JsStringEscape.md | 9 | ||||
-rw-r--r-- | docs/PursLoader/Loader.md | 27 | ||||
-rw-r--r-- | docs/PursLoader/LoaderRef.md | 51 | ||||
-rw-r--r-- | docs/PursLoader/LoaderUtil.md | 9 | ||||
-rw-r--r-- | docs/PursLoader/Options.md | 21 | ||||
-rw-r--r-- | docs/PursLoader/Path.md | 27 | ||||
-rw-r--r-- | docs/PursLoader/Plugin.md | 33 | ||||
-rw-r--r-- | entry.js | 11 | ||||
-rw-r--r-- | package.json | 60 | ||||
-rw-r--r-- | src/PursLoader/Debug.js | 12 | ||||
-rw-r--r-- | src/PursLoader/Debug.purs | 9 | ||||
-rw-r--r-- | src/PursLoader/JsStringEscape.js | 7 | ||||
-rw-r--r-- | src/PursLoader/JsStringEscape.purs | 3 | ||||
-rw-r--r-- | src/PursLoader/Loader.purs | 108 | ||||
-rw-r--r-- | src/PursLoader/LoaderRef.js | 50 | ||||
-rw-r--r-- | src/PursLoader/LoaderRef.purs | 40 | ||||
-rw-r--r-- | src/PursLoader/Path.js | 24 | ||||
-rw-r--r-- | src/PursLoader/Path.purs | 14 | ||||
-rw-r--r-- | src/PursLoader/Plugin.js | 14 | ||||
-rw-r--r-- | src/PursLoader/Plugin.purs | 34 | ||||
-rw-r--r-- | src/index.js | 481 | ||||
-rw-r--r-- | webpack.config.js | 32 |
28 files changed, 582 insertions, 585 deletions
diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..9d8d516 --- /dev/null +++ b/.babelrc | |||
@@ -0,0 +1 @@ | |||
{ "presets": ["es2015"] } | |||
@@ -1,9 +1,4 @@ | |||
1 | .psci | 1 | **DS_Store* |
2 | .pulp-cache | 2 | build/ |
3 | npm-debug.log | ||
4 | index.json | ||
5 | index.js | ||
6 | node_modules/ | 3 | node_modules/ |
7 | bower_components/ | 4 | bower_components/ |
8 | build/ | ||
9 | tmp/ | ||
@@ -1,4 +1,4 @@ | |||
1 | Copyright (c) 2015 Eric Thul | 1 | Copyright (c) 2016 Alexander Mingoia and Eric Thul |
2 | 2 | ||
3 | Permission is hereby granted, free of charge, to any person obtaining a | 3 | Permission is hereby granted, free of charge, to any person obtaining a |
4 | copy of this software and associated documentation files (the | 4 | copy of this software and associated documentation files (the |
@@ -2,16 +2,70 @@ | |||
2 | 2 | ||
3 | > [PureScript](http://www.purescript.org) loader for [webpack](http://webpack.github.io) | 3 | > [PureScript](http://www.purescript.org) loader for [webpack](http://webpack.github.io) |
4 | 4 | ||
5 | - Supports hot-reloading and rebuilding of single source files | ||
6 | - Dead code elimination using the `bundle` option | ||
7 | - Colorized build output using `purescript-psa` and the `psc: "psa"` option | ||
8 | |||
5 | ## Install | 9 | ## Install |
6 | 10 | ||
7 | Install with [npm](https://npmjs.org/package/purs-loader). | 11 | Install with [npm](https://npmjs.org/package/purs-loader). |
8 | 12 | ||
9 | This loader works in conjunction with the [PureScript webpack plugin](https://npmjs.org/package/purescript-webpack-plugin). Ensure the plugin is installed and configured accordingly. | ||
10 | |||
11 | ``` | 13 | ``` |
12 | npm install purs-loader --save-dev | 14 | npm install purs-loader --save-dev |
13 | ``` | 15 | ``` |
14 | 16 | ||
15 | ## Example | 17 | ## Example |
16 | 18 | ||
17 | Refer to the [purescript-webpack-example](https://github.com/ethul/purescript-webpack-example) for an example. | 19 | ```javascript |
20 | const webpackConfig = { | ||
21 | // ... | ||
22 | loaders: [ | ||
23 | // ... | ||
24 | { | ||
25 | test: /\.purs$/, | ||
26 | loader: 'purs-loader', | ||
27 | exclude: /node_modules/, | ||
28 | query: { | ||
29 | psc: 'psa', | ||
30 | src: ['bower_components/purescript-*/src/**/*.purs', 'src/**/*.purs'], | ||
31 | ffi: ['bower_components/purescript-*/src/**/*.js', 'src/**/*.js'], | ||
32 | } | ||
33 | } | ||
34 | // ... | ||
35 | ] | ||
36 | // ... | ||
37 | } | ||
38 | ``` | ||
39 | |||
40 | ### Options | ||
41 | |||
42 | Default options: | ||
43 | |||
44 | ```javascript | ||
45 | { | ||
46 | psc: 'psc', | ||
47 | pscArgs: {}, | ||
48 | pscBundle: 'psc-bundle', | ||
49 | pscBundleArgs: {}, | ||
50 | pscIde: false, // instant rebuilds using psc-ide-server (experimental) | ||
51 | pscIdeColors: false, // defaults to true if psc === 'psa' | ||
52 | bundleOutput: 'output/bundle.js', | ||
53 | bundleNamespace: 'PS', | ||
54 | bundle: false, | ||
55 | warnings: true, | ||
56 | output: 'output', | ||
57 | src: [ | ||
58 | path.join('src', '**', '*.purs'), | ||
59 | path.join('bower_components', 'purescript-*', 'src', '**', '*.purs') | ||
60 | ], | ||
61 | ffi: [ | ||
62 | path.join('src', '**', '*.js'), | ||
63 | path.join('bower_components', 'purescript-*', 'src', '**', '*.js') | ||
64 | ], | ||
65 | } | ||
66 | ``` | ||
67 | |||
68 | ### Instant rebuilds (experimental) | ||
69 | |||
70 | Experimental support for instant rebuilds using `psc-ide-server` can be enabled | ||
71 | via the `pscIde: true` option. | ||
diff --git a/bower.json b/bower.json deleted file mode 100644 index 761c24c..0000000 --- a/bower.json +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | { | ||
2 | "name": "purs-loader", | ||
3 | "private": true, | ||
4 | "dependencies": { | ||
5 | "purescript-aff": "^0.13.0", | ||
6 | "purescript-foreign": "^0.7.0", | ||
7 | "purescript-unsafe-coerce": "~0.1.0", | ||
8 | "purescript-nullable": "~0.2.1" | ||
9 | } | ||
10 | } | ||
diff --git a/docs/PursLoader/Debug.md b/docs/PursLoader/Debug.md deleted file mode 100644 index 824a9f8..0000000 --- a/docs/PursLoader/Debug.md +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | ## Module PursLoader.Debug | ||
2 | |||
3 | #### `debug` | ||
4 | |||
5 | ``` purescript | ||
6 | debug :: forall eff. String -> Eff (loader :: Loader | eff) Unit | ||
7 | ``` | ||
8 | |||
9 | |||
diff --git a/docs/PursLoader/JsStringEscape.md b/docs/PursLoader/JsStringEscape.md deleted file mode 100644 index 09f52aa..0000000 --- a/docs/PursLoader/JsStringEscape.md +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | ## Module PursLoader.JsStringEscape | ||
2 | |||
3 | #### `jsStringEscape` | ||
4 | |||
5 | ``` purescript | ||
6 | jsStringEscape :: String -> String | ||
7 | ``` | ||
8 | |||
9 | |||
diff --git a/docs/PursLoader/Loader.md b/docs/PursLoader/Loader.md deleted file mode 100644 index d05e3b7..0000000 --- a/docs/PursLoader/Loader.md +++ /dev/null | |||
@@ -1,27 +0,0 @@ | |||
1 | ## Module PursLoader.Loader | ||
2 | |||
3 | #### `Effects` | ||
4 | |||
5 | ``` purescript | ||
6 | type Effects eff = (console :: CONSOLE, err :: EXCEPTION | eff) | ||
7 | ``` | ||
8 | |||
9 | #### `Effects_` | ||
10 | |||
11 | ``` purescript | ||
12 | type Effects_ eff = Effects (loader :: Loader | eff) | ||
13 | ``` | ||
14 | |||
15 | #### `loader` | ||
16 | |||
17 | ``` purescript | ||
18 | loader :: forall eff. LoaderRef -> String -> Eff (Effects_ eff) Unit | ||
19 | ``` | ||
20 | |||
21 | #### `loaderFn` | ||
22 | |||
23 | ``` purescript | ||
24 | loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects_ eff) Unit) | ||
25 | ``` | ||
26 | |||
27 | |||
diff --git a/docs/PursLoader/LoaderRef.md b/docs/PursLoader/LoaderRef.md deleted file mode 100644 index 917db3a..0000000 --- a/docs/PursLoader/LoaderRef.md +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | ## Module PursLoader.LoaderRef | ||
2 | |||
3 | #### `AsyncCallback` | ||
4 | |||
5 | ``` purescript | ||
6 | type AsyncCallback eff = Maybe Error -> String -> Eff (loader :: Loader | eff) Unit | ||
7 | ``` | ||
8 | |||
9 | #### `LoaderRef` | ||
10 | |||
11 | ``` purescript | ||
12 | data LoaderRef | ||
13 | ``` | ||
14 | |||
15 | #### `Loader` | ||
16 | |||
17 | ``` purescript | ||
18 | data Loader :: ! | ||
19 | ``` | ||
20 | |||
21 | #### `async` | ||
22 | |||
23 | ``` purescript | ||
24 | async :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) (Maybe Error -> String -> Eff (loader :: Loader | eff) Unit) | ||
25 | ``` | ||
26 | |||
27 | #### `cacheable` | ||
28 | |||
29 | ``` purescript | ||
30 | cacheable :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit | ||
31 | ``` | ||
32 | |||
33 | #### `clearDependencies` | ||
34 | |||
35 | ``` purescript | ||
36 | clearDependencies :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit | ||
37 | ``` | ||
38 | |||
39 | #### `resourcePath` | ||
40 | |||
41 | ``` purescript | ||
42 | resourcePath :: LoaderRef -> String | ||
43 | ``` | ||
44 | |||
45 | #### `addDependency` | ||
46 | |||
47 | ``` purescript | ||
48 | addDependency :: forall eff. LoaderRef -> String -> Eff (loader :: Loader | eff) Unit | ||
49 | ``` | ||
50 | |||
51 | |||
diff --git a/docs/PursLoader/LoaderUtil.md b/docs/PursLoader/LoaderUtil.md deleted file mode 100644 index 36d6879..0000000 --- a/docs/PursLoader/LoaderUtil.md +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | ## Module PursLoader.LoaderUtil | ||
2 | |||
3 | #### `parseQuery` | ||
4 | |||
5 | ``` purescript | ||
6 | parseQuery :: String -> Foreign | ||
7 | ``` | ||
8 | |||
9 | |||
diff --git a/docs/PursLoader/Options.md b/docs/PursLoader/Options.md deleted file mode 100644 index b3352fc..0000000 --- a/docs/PursLoader/Options.md +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | ## Module PursLoader.Options | ||
2 | |||
3 | #### `Options` | ||
4 | |||
5 | ``` purescript | ||
6 | newtype Options | ||
7 | = Options { bundleOutput :: String } | ||
8 | ``` | ||
9 | |||
10 | ##### Instances | ||
11 | ``` purescript | ||
12 | IsForeign Options | ||
13 | ``` | ||
14 | |||
15 | #### `runOptions` | ||
16 | |||
17 | ``` purescript | ||
18 | runOptions :: Options -> Options_ | ||
19 | ``` | ||
20 | |||
21 | |||
diff --git a/docs/PursLoader/Path.md b/docs/PursLoader/Path.md deleted file mode 100644 index cc00436..0000000 --- a/docs/PursLoader/Path.md +++ /dev/null | |||
@@ -1,27 +0,0 @@ | |||
1 | ## Module PursLoader.Path | ||
2 | |||
3 | #### `relative` | ||
4 | |||
5 | ``` purescript | ||
6 | relative :: String -> String -> String | ||
7 | ``` | ||
8 | |||
9 | #### `resolve` | ||
10 | |||
11 | ``` purescript | ||
12 | resolve :: String -> String | ||
13 | ``` | ||
14 | |||
15 | #### `dirname` | ||
16 | |||
17 | ``` purescript | ||
18 | dirname :: String -> String | ||
19 | ``` | ||
20 | |||
21 | #### `joinPath` | ||
22 | |||
23 | ``` purescript | ||
24 | joinPath :: String -> String -> String | ||
25 | ``` | ||
26 | |||
27 | |||
diff --git a/docs/PursLoader/Plugin.md b/docs/PursLoader/Plugin.md deleted file mode 100644 index 7a524da..0000000 --- a/docs/PursLoader/Plugin.md +++ /dev/null | |||
@@ -1,33 +0,0 @@ | |||
1 | ## Module PursLoader.Plugin | ||
2 | |||
3 | #### `Compile` | ||
4 | |||
5 | ``` purescript | ||
6 | type Compile eff = Nullable Error -> DependencyGraph -> Eff eff Unit | ||
7 | ``` | ||
8 | |||
9 | #### `Context` | ||
10 | |||
11 | ``` purescript | ||
12 | type Context eff = { compile :: Compile eff -> Eff eff Unit, options :: Options } | ||
13 | ``` | ||
14 | |||
15 | #### `Options` | ||
16 | |||
17 | ``` purescript | ||
18 | type Options = { bundle :: Boolean, output :: String, bundleOutput :: String } | ||
19 | ``` | ||
20 | |||
21 | #### `dependenciesOf` | ||
22 | |||
23 | ``` purescript | ||
24 | dependenciesOf :: DependencyGraph -> String -> Either Error (Array String) | ||
25 | ``` | ||
26 | |||
27 | #### `DependencyGraph` | ||
28 | |||
29 | ``` purescript | ||
30 | data DependencyGraph :: * | ||
31 | ``` | ||
32 | |||
33 | |||
diff --git a/entry.js b/entry.js deleted file mode 100644 index 87f52d3..0000000 --- a/entry.js +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | var pursLoader = require('PursLoader.Loader'); | ||
4 | |||
5 | function loader(source) { | ||
6 | var ref = this; | ||
7 | var result = pursLoader.loaderFn(ref, source); | ||
8 | return result(); | ||
9 | } | ||
10 | |||
11 | module.exports = loader; | ||
diff --git a/package.json b/package.json index 6ab55ec..d3d807f 100644 --- a/package.json +++ b/package.json | |||
@@ -1,30 +1,50 @@ | |||
1 | { | 1 | { |
2 | "name": "purs-loader", | 2 | "name": "purs-loader", |
3 | "version": "0.6.0", | 3 | "version": "1.0.0", |
4 | "description": "PureScript loader for webpack", | 4 | "description": "A webpack loader for PureScript.", |
5 | "license": "MIT", | 5 | "main": "index.js", |
6 | "repository": "ethul/purs-loader", | ||
7 | "author": { | ||
8 | "name": "Eric Thul", | ||
9 | "email": "thul.eric@gmail.com" | ||
10 | }, | ||
11 | "scripts": { | ||
12 | "build": "npm run-script build:compile && npm run-script build:docs && npm run-script build:package", | ||
13 | "build:compile": "pulp build -o build --force", | ||
14 | "build:docs": "pulp docs", | ||
15 | "build:package": "webpack --progress --colors --profile --bail", | ||
16 | "build:watch": "pulp -w build -o build --force", | ||
17 | "build:json": "webpack --progress --colors --profile --bail --json > index.json", | ||
18 | "prepublish": "npm run-script build" | ||
19 | }, | ||
20 | "files": [ | 6 | "files": [ |
21 | "index.js" | 7 | "index.js" |
22 | ], | 8 | ], |
23 | "devDependencies": { | 9 | "scripts": { |
24 | "webpack": "^1.8.4" | 10 | "build": "babel src/index.js -o index.js", |
11 | "prepublish": "npm run build", | ||
12 | "test": "echo \"Error: no test specified\" && exit 1" | ||
13 | }, | ||
14 | "repository": { | ||
15 | "type": "git", | ||
16 | "url": "git://github.com/alexmingoia/purs-loader.git" | ||
17 | }, | ||
18 | "keywords": [ | ||
19 | "loader", | ||
20 | "webpack", | ||
21 | "purescript", | ||
22 | "purs-loader", | ||
23 | "purs-loader" | ||
24 | ], | ||
25 | "author": "Alexander C. Mingoia", | ||
26 | "contributors": [ | ||
27 | "Eric Thul" | ||
28 | ], | ||
29 | "license": "MIT", | ||
30 | "bugs": { | ||
31 | "url": "https://github.com/alexmingoia/purs-loader/issues" | ||
32 | }, | ||
33 | "homepage": "https://github.com/alexmingoia/purs-loader#readme", | ||
34 | "peerDependencies": { | ||
35 | "webpack": ">=1.0.0 <3.0.0", | ||
36 | "purescript": ">=0.8.0" | ||
25 | }, | 37 | }, |
26 | "dependencies": { | 38 | "dependencies": { |
39 | "bluebird": "^3.3.5", | ||
40 | "chalk": "^1.1.3", | ||
27 | "debug": "^2.2.0", | 41 | "debug": "^2.2.0", |
28 | "js-string-escape": "^1.0.1" | 42 | "globby": "^4.0.0", |
43 | "loader-utils": "^0.2.14", | ||
44 | "promise-retry": "^1.1.0" | ||
45 | }, | ||
46 | "devDependencies": { | ||
47 | "babel-cli": "^6.8.0", | ||
48 | "babel-preset-es2015": "^6.6.0" | ||
29 | } | 49 | } |
30 | } | 50 | } |
diff --git a/src/PursLoader/Debug.js b/src/PursLoader/Debug.js deleted file mode 100644 index 85eca10..0000000 --- a/src/PursLoader/Debug.js +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | // module PursLoader.Debug | ||
4 | |||
5 | var debug_ = require('debug')('purs-loader'); | ||
6 | |||
7 | function debug(message) { | ||
8 | return function(){ | ||
9 | debug_(message); | ||
10 | }; | ||
11 | } | ||
12 | exports.debug = debug; | ||
diff --git a/src/PursLoader/Debug.purs b/src/PursLoader/Debug.purs deleted file mode 100644 index 7a02f69..0000000 --- a/src/PursLoader/Debug.purs +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | module PursLoader.Debug (debug) where | ||
2 | |||
3 | import Prelude (Unit()) | ||
4 | |||
5 | import Control.Monad.Eff (Eff()) | ||
6 | |||
7 | import PursLoader.LoaderRef (Loader()) | ||
8 | |||
9 | foreign import debug :: forall eff. String -> Eff (loader :: Loader | eff) Unit | ||
diff --git a/src/PursLoader/JsStringEscape.js b/src/PursLoader/JsStringEscape.js deleted file mode 100644 index ff0a1a6..0000000 --- a/src/PursLoader/JsStringEscape.js +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | // module PursLoader.JsStringEscape | ||
4 | |||
5 | var jsStringEscape = require('js-string-escape'); | ||
6 | |||
7 | exports.jsStringEscape = jsStringEscape; | ||
diff --git a/src/PursLoader/JsStringEscape.purs b/src/PursLoader/JsStringEscape.purs deleted file mode 100644 index 79590ae..0000000 --- a/src/PursLoader/JsStringEscape.purs +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | module PursLoader.JsStringEscape (jsStringEscape) where | ||
2 | |||
3 | foreign import jsStringEscape :: String -> String | ||
diff --git a/src/PursLoader/Loader.purs b/src/PursLoader/Loader.purs deleted file mode 100644 index acb0993..0000000 --- a/src/PursLoader/Loader.purs +++ /dev/null | |||
@@ -1,108 +0,0 @@ | |||
1 | module PursLoader.Loader | ||
2 | ( Effects() | ||
3 | , Effects_() | ||
4 | , loader | ||
5 | , loaderFn | ||
6 | ) where | ||
7 | |||
8 | import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (++), (<<<), bind, const, id, pure, unit) | ||
9 | |||
10 | import Control.Bind (join) | ||
11 | import Control.Monad.Eff (Eff(), foreachE) | ||
12 | import Control.Monad.Eff.Console (CONSOLE()) | ||
13 | import Control.Monad.Eff.Exception (EXCEPTION(), Error(), error) | ||
14 | |||
15 | import Data.Array ((!!)) | ||
16 | import Data.Either (Either(..), either) | ||
17 | import Data.Function (Fn2(), mkFn2) | ||
18 | import Data.Maybe (maybe) | ||
19 | import Data.Nullable (toMaybe) | ||
20 | import Data.String.Regex (Regex(), match, noFlags, regex) | ||
21 | |||
22 | import Unsafe.Coerce (unsafeCoerce) | ||
23 | |||
24 | import PursLoader.Debug (debug) | ||
25 | import PursLoader.JsStringEscape (jsStringEscape) | ||
26 | import PursLoader.LoaderRef | ||
27 | ( AsyncCallback() | ||
28 | , LoaderRef() | ||
29 | , Loader() | ||
30 | , async | ||
31 | , cacheable | ||
32 | , addDependency | ||
33 | , resourcePath | ||
34 | ) | ||
35 | import PursLoader.Path (dirname, joinPath, relative) | ||
36 | import PursLoader.Plugin as Plugin | ||
37 | |||
38 | type Effects eff = (console :: CONSOLE, err :: EXCEPTION | eff) | ||
39 | |||
40 | type Effects_ eff = Effects (loader :: Loader | eff) | ||
41 | |||
42 | loader :: forall eff. LoaderRef -> String -> Eff (Effects_ eff) Unit | ||
43 | loader ref source = do | ||
44 | callback <- async ref | ||
45 | |||
46 | cacheable ref | ||
47 | |||
48 | debug "Invoke PureScript plugin compilation" | ||
49 | |||
50 | pluginContext.compile (compile callback) | ||
51 | where | ||
52 | pluginContext :: Plugin.Context (Effects_ eff) | ||
53 | pluginContext = (unsafeCoerce ref).purescriptWebpackPluginContext | ||
54 | |||
55 | compile :: AsyncCallback (Effects eff) -> Plugin.Compile (Effects_ eff) | ||
56 | compile callback error' graph = do | ||
57 | either (const $ pure unit) (\a -> debug ("Adding PureScript dependency " ++ a)) name | ||
58 | |||
59 | addDependency ref (resourcePath ref) | ||
60 | |||
61 | either (const $ callback (pure fixedError) "") id | ||
62 | (handle <$> name <*> dependencies <*> exports) | ||
63 | where | ||
64 | fixedError :: Error | ||
65 | fixedError = error "PureScript compilation has failed." | ||
66 | |||
67 | handle :: String -> Array String -> String -> Eff (Effects_ eff) Unit | ||
68 | handle name' deps res = do | ||
69 | debug ("Adding PureScript dependencies for " ++ name') | ||
70 | foreachE deps (addDependency ref) | ||
71 | debug "Generated loader result" | ||
72 | debug res | ||
73 | callback (const fixedError <$> toMaybe error') res | ||
74 | |||
75 | exports :: Either Error String | ||
76 | exports = | ||
77 | if pluginContext.options.bundle | ||
78 | then bundleExport <$> name | ||
79 | else moduleExport <<< modulePath <$> name | ||
80 | where | ||
81 | bundleExport :: String -> String | ||
82 | bundleExport name' = "module.exports = require('" ++ jsStringEscape path ++ "')['" ++ name' ++ "'];" | ||
83 | where | ||
84 | path :: String | ||
85 | path = relative resourceDir pluginContext.options.bundleOutput | ||
86 | |||
87 | moduleExport :: String -> String | ||
88 | moduleExport path = "module.exports = require('" ++ jsStringEscape path ++ "');" | ||
89 | |||
90 | modulePath :: String -> String | ||
91 | modulePath = relative resourceDir <<< joinPath pluginContext.options.output | ||
92 | |||
93 | resourceDir :: String | ||
94 | resourceDir = dirname (resourcePath ref) | ||
95 | |||
96 | dependencies :: Either Error (Array String) | ||
97 | dependencies = Plugin.dependenciesOf graph (resourcePath ref) | ||
98 | |||
99 | name :: Either Error String | ||
100 | name = | ||
101 | maybe (Left $ error "Failed to parse module name") Right | ||
102 | (join $ match re source >>= \as -> as !! 1) | ||
103 | where | ||
104 | re :: Regex | ||
105 | re = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true } | ||
106 | |||
107 | loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects_ eff) Unit) | ||
108 | loaderFn = mkFn2 loader | ||
diff --git a/src/PursLoader/LoaderRef.js b/src/PursLoader/LoaderRef.js deleted file mode 100644 index a5d8e1f..0000000 --- a/src/PursLoader/LoaderRef.js +++ /dev/null | |||
@@ -1,50 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | // module PursLoader.LoaderRef | ||
4 | |||
5 | function asyncFn(isJust, fromMaybe, ref){ | ||
6 | return function(){ | ||
7 | var callback = ref.async(); | ||
8 | return function(error){ | ||
9 | return function(value){ | ||
10 | return function(){ | ||
11 | return isJust(error) ? callback(fromMaybe(new Error())(error)) | ||
12 | : callback(null, value); | ||
13 | }; | ||
14 | }; | ||
15 | }; | ||
16 | }; | ||
17 | } | ||
18 | function cacheable(ref){ | ||
19 | return function(){ | ||
20 | return ref.cacheable && ref.cacheable(); | ||
21 | }; | ||
22 | } | ||
23 | |||
24 | function clearDependencies(ref){ | ||
25 | return function(){ | ||
26 | return ref.clearDependencies(); | ||
27 | }; | ||
28 | } | ||
29 | |||
30 | function resourcePath(ref){ | ||
31 | return ref.resourcePath; | ||
32 | } | ||
33 | |||
34 | function addDependency(ref){ | ||
35 | return function(dep){ | ||
36 | return function(){ | ||
37 | return ref.addDependency(dep); | ||
38 | }; | ||
39 | }; | ||
40 | } | ||
41 | |||
42 | exports.asyncFn = asyncFn; | ||
43 | |||
44 | exports.cacheable = cacheable; | ||
45 | |||
46 | exports.clearDependencies = clearDependencies; | ||
47 | |||
48 | exports.resourcePath = resourcePath; | ||
49 | |||
50 | exports.addDependency = addDependency; | ||
diff --git a/src/PursLoader/LoaderRef.purs b/src/PursLoader/LoaderRef.purs deleted file mode 100644 index 140d94a..0000000 --- a/src/PursLoader/LoaderRef.purs +++ /dev/null | |||
@@ -1,40 +0,0 @@ | |||
1 | module PursLoader.LoaderRef | ||
2 | ( LoaderRef() | ||
3 | , Loader() | ||
4 | , AsyncCallback() | ||
5 | , async | ||
6 | , cacheable | ||
7 | , clearDependencies | ||
8 | , addDependency | ||
9 | , resourcePath | ||
10 | ) where | ||
11 | |||
12 | import Prelude (Unit()) | ||
13 | |||
14 | import Control.Monad.Eff (Eff()) | ||
15 | import Control.Monad.Eff.Exception (Error()) | ||
16 | |||
17 | import Data.Function (Fn3(), runFn3) | ||
18 | import Data.Maybe (Maybe(), fromMaybe, isJust) | ||
19 | |||
20 | type AsyncCallback eff = Maybe Error -> String -> Eff (loader :: Loader | eff) Unit | ||
21 | |||
22 | data LoaderRef | ||
23 | |||
24 | foreign import data Loader :: ! | ||
25 | |||
26 | foreign import asyncFn :: forall eff. Fn3 (Maybe Error -> Boolean) | ||
27 | (Error -> Maybe Error -> Error) | ||
28 | LoaderRef | ||
29 | (Eff (loader :: Loader | eff) (AsyncCallback eff)) | ||
30 | |||
31 | async :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) (Maybe Error -> String -> Eff (loader :: Loader | eff) Unit) | ||
32 | async ref = runFn3 asyncFn isJust fromMaybe ref | ||
33 | |||
34 | foreign import cacheable :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit | ||
35 | |||
36 | foreign import clearDependencies :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit | ||
37 | |||
38 | foreign import resourcePath :: LoaderRef -> String | ||
39 | |||
40 | foreign import addDependency :: forall eff. LoaderRef -> String -> Eff (loader :: Loader | eff) Unit | ||
diff --git a/src/PursLoader/Path.js b/src/PursLoader/Path.js deleted file mode 100644 index 878f256..0000000 --- a/src/PursLoader/Path.js +++ /dev/null | |||
@@ -1,24 +0,0 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | // module PursLoader.Path | ||
4 | |||
5 | var path = require('path'); | ||
6 | |||
7 | function relative(from) { | ||
8 | return function(to){ | ||
9 | return path.relative(from, to); | ||
10 | }; | ||
11 | } | ||
12 | exports.relative = relative; | ||
13 | |||
14 | |||
15 | function joinPath(a) { | ||
16 | return function(b) { | ||
17 | return path.join(a, b); | ||
18 | }; | ||
19 | } | ||
20 | exports.joinPath = joinPath; | ||
21 | |||
22 | exports.resolve = path.resolve; | ||
23 | |||
24 | exports.dirname = path.dirname; | ||
diff --git a/src/PursLoader/Path.purs b/src/PursLoader/Path.purs deleted file mode 100644 index 98cad5a..0000000 --- a/src/PursLoader/Path.purs +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | module PursLoader.Path | ||
2 | ( relative | ||
3 | , resolve | ||
4 | , dirname | ||
5 | , joinPath | ||
6 | ) where | ||
7 | |||
8 | foreign import relative :: String -> String -> String | ||
9 | |||
10 | foreign import resolve :: String -> String | ||
11 | |||
12 | foreign import dirname :: String -> String | ||
13 | |||
14 | foreign import joinPath :: String -> String -> String | ||
diff --git a/src/PursLoader/Plugin.js b/src/PursLoader/Plugin.js deleted file mode 100644 index ded6df5..0000000 --- a/src/PursLoader/Plugin.js +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | // module PursLoader.Plugin | ||
4 | |||
5 | function dependenciesOfFn(left, right, graph, node) { | ||
6 | try { | ||
7 | var dependencies = graph.dependenciesOf(node); | ||
8 | return right(dependencies); | ||
9 | } | ||
10 | catch (error) { | ||
11 | return left(error); | ||
12 | } | ||
13 | } | ||
14 | exports.dependenciesOfFn = dependenciesOfFn; | ||
diff --git a/src/PursLoader/Plugin.purs b/src/PursLoader/Plugin.purs deleted file mode 100644 index c798c83..0000000 --- a/src/PursLoader/Plugin.purs +++ /dev/null | |||
@@ -1,34 +0,0 @@ | |||
1 | module PursLoader.Plugin | ||
2 | ( Compile() | ||
3 | , Context() | ||
4 | , Options() | ||
5 | , DependencyGraph() | ||
6 | , dependenciesOf | ||
7 | ) where | ||
8 | |||
9 | import Prelude (Unit()) | ||
10 | |||
11 | import Control.Monad.Eff (Eff()) | ||
12 | import Control.Monad.Eff.Exception (Error()) | ||
13 | |||
14 | import Data.Either (Either(..)) | ||
15 | import Data.Function (Fn4(), runFn4) | ||
16 | import Data.Nullable (Nullable()) | ||
17 | |||
18 | type Compile eff = Nullable Error -> DependencyGraph -> Eff eff Unit | ||
19 | |||
20 | type Context eff = { compile :: Compile eff -> Eff eff Unit, options :: Options } | ||
21 | |||
22 | type Options = { bundle :: Boolean, output :: String, bundleOutput :: String } | ||
23 | |||
24 | dependenciesOf :: DependencyGraph -> String -> Either Error (Array String) | ||
25 | dependenciesOf = runFn4 dependenciesOfFn Left Right | ||
26 | |||
27 | foreign import data DependencyGraph :: * | ||
28 | |||
29 | foreign import dependenciesOfFn | ||
30 | :: Fn4 (Error -> Either Error (Array String)) | ||
31 | (Array String -> Either Error (Array String)) | ||
32 | DependencyGraph | ||
33 | String | ||
34 | (Either Error (Array String)) | ||
diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..72da4d0 --- /dev/null +++ b/src/index.js | |||
@@ -0,0 +1,481 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | const colors = require('chalk') | ||
4 | const debug = require('debug')('purs-loader') | ||
5 | const loaderUtils = require('loader-utils') | ||
6 | const globby = require('globby') | ||
7 | const Promise = require('bluebird') | ||
8 | const fs = Promise.promisifyAll(require('fs')) | ||
9 | const spawn = require('child_process').spawn | ||
10 | const path = require('path') | ||
11 | const retryPromise = require('promise-retry') | ||
12 | |||
13 | const ffiModuleRegex = /\/\/\s+module\s+([\w\.]+)/i | ||
14 | const srcModuleRegex = /(?:^|\n)module\s+([\w\.]+)/i | ||
15 | const requireRegex = /require\(['"]\.\.\/([\w\.]+)['"]\)/g | ||
16 | |||
17 | module.exports = function purescriptLoader(source, map) { | ||
18 | const callback = this.async() | ||
19 | const config = this.options | ||
20 | const query = loaderUtils.parseQuery(this.query) | ||
21 | const webpackOptions = this.options.purescriptLoader || {} | ||
22 | |||
23 | const options = Object.assign({ | ||
24 | context: config.context, | ||
25 | psc: 'psc', | ||
26 | pscArgs: {}, | ||
27 | pscBundle: 'psc-bundle', | ||
28 | pscBundleArgs: {}, | ||
29 | pscIde: false, | ||
30 | pscIdeColors: webpackOptions.psc === 'psa' || query.psc === 'psa', | ||
31 | pscIdeArgs: {}, | ||
32 | bundleOutput: 'output/bundle.js', | ||
33 | bundleNamespace: 'PS', | ||
34 | bundle: false, | ||
35 | warnings: true, | ||
36 | output: 'output', | ||
37 | src: [ | ||
38 | path.join('src', '**', '*.purs'), | ||
39 | path.join('bower_components', 'purescript-*', 'src', '**', '*.purs') | ||
40 | ], | ||
41 | ffi: [ | ||
42 | path.join('src', '**', '*.js'), | ||
43 | path.join('bower_components', 'purescript-*', 'src', '**', '*.js') | ||
44 | ], | ||
45 | }, webpackOptions, query) | ||
46 | |||
47 | this.cacheable && this.cacheable() | ||
48 | |||
49 | let cache = config.purescriptLoaderCache = config.purescriptLoaderCache || { | ||
50 | rebuild: false, | ||
51 | deferred: [], | ||
52 | bundleModules: [], | ||
53 | } | ||
54 | |||
55 | if (!config.purescriptLoaderInstalled) { | ||
56 | config.purescriptLoaderInstalled = true | ||
57 | |||
58 | // invalidate loader cache when bundle is marked as invalid (in watch mode) | ||
59 | this._compiler.plugin('invalid', () => { | ||
60 | cache = config.purescriptLoaderCache = { | ||
61 | rebuild: options.pscIde, | ||
62 | deferred: [], | ||
63 | ideServer: cache.ideServer | ||
64 | } | ||
65 | }) | ||
66 | |||
67 | // add psc warnings to webpack compilation warnings | ||
68 | this._compiler.plugin('after-compile', (compilation, callback) => { | ||
69 | if (options.warnings && cache.warnings) { | ||
70 | compilation.warnings.unshift(`PureScript compilation:\n${cache.warnings}`) | ||
71 | } | ||
72 | |||
73 | if (cache.errors) { | ||
74 | compilation.errors.unshift(`PureScript compilation:\n${cache.errors}`) | ||
75 | } | ||
76 | |||
77 | callback() | ||
78 | }) | ||
79 | } | ||
80 | |||
81 | const psModuleName = match(srcModuleRegex, source) | ||
82 | const psModule = { | ||
83 | name: psModuleName, | ||
84 | load: js => callback(null, js), | ||
85 | reject: error => callback(error), | ||
86 | srcPath: this.resourcePath, | ||
87 | srcDir: path.dirname(this.resourcePath), | ||
88 | jsPath: path.resolve(path.join(options.output, psModuleName, 'index.js')), | ||
89 | options: options, | ||
90 | cache: cache, | ||
91 | } | ||
92 | |||
93 | debug('loader called', psModule.name) | ||
94 | |||
95 | if (options.bundle) { | ||
96 | cache.bundleModules.push(psModule.name) | ||
97 | } | ||
98 | |||
99 | if (cache.rebuild) { | ||
100 | return connectIdeServer(psModule) | ||
101 | .then(rebuild) | ||
102 | .then(toJavaScript) | ||
103 | .then(psModule.load) | ||
104 | .catch(psModule.reject) | ||
105 | } | ||
106 | |||
107 | if (cache.compilationFinished) { | ||
108 | return toJavaScript(psModule).then(psModule.load).catch(psModule.reject) | ||
109 | } | ||
110 | |||
111 | // We need to wait for compilation to finish before the loaders run so that | ||
112 | // references to compiled output are valid. | ||
113 | cache.deferred.push(psModule) | ||
114 | |||
115 | if (!cache.compilationStarted) { | ||
116 | return compile(psModule) | ||
117 | .then(() => Promise.map(cache.deferred, psModule => { | ||
118 | if (typeof cache.ideServer === 'object') cache.ideServer.kill() | ||
119 | return toJavaScript(psModule).then(psModule.load) | ||
120 | })) | ||
121 | .catch(error => { | ||
122 | cache.deferred[0].reject(error) | ||
123 | cache.deferred.slice(1).forEach(psModule => psModule.reject(true)) | ||
124 | }) | ||
125 | } | ||
126 | } | ||
127 | |||
128 | // The actual loader is executed *after* purescript compilation. | ||
129 | function toJavaScript(psModule) { | ||
130 | const options = psModule.options | ||
131 | const cache = psModule.cache | ||
132 | const bundlePath = path.resolve(options.bundleOutput) | ||
133 | const jsPath = cache.bundle ? bundlePath : psModule.jsPath | ||
134 | |||
135 | debug('loading JavaScript for', psModule.name) | ||
136 | |||
137 | return Promise.props({ | ||
138 | js: fs.readFileAsync(jsPath, 'utf8'), | ||
139 | psModuleMap: psModuleMap(options, cache) | ||
140 | }).then(result => { | ||
141 | let js = '' | ||
142 | |||
143 | if (options.bundle) { | ||
144 | // if bundling, return a reference to the bundle | ||
145 | js = 'module.exports = require("' | ||
146 | + path.relative(psModule.srcDir, options.bundleOutput) | ||
147 | + '")["' + psModule.name + '"]' | ||
148 | } else { | ||
149 | // replace require paths to output files generated by psc with paths | ||
150 | // to purescript sources, which are then also run through this loader. | ||
151 | js = result.js | ||
152 | .replace(requireRegex, (m, p1) => { | ||
153 | return 'require("' + result.psModuleMap[p1].src + '")' | ||
154 | }) | ||
155 | .replace(/require\(['"]\.\/foreign['"]\)/g, (m, p1) => { | ||
156 | return 'require("' + result.psModuleMap[psModule.name].ffi + '")' | ||
157 | }) | ||
158 | } | ||
159 | |||
160 | return js | ||
161 | }) | ||
162 | } | ||
163 | |||
164 | function compile(psModule) { | ||
165 | const options = psModule.options | ||
166 | const cache = psModule.cache | ||
167 | const stderr = [] | ||
168 | |||
169 | if (cache.compilationStarted) return Promise.resolve(psModule) | ||
170 | |||
171 | cache.compilationStarted = true | ||
172 | |||
173 | const args = dargs(Object.assign({ | ||
174 | _: options.src, | ||
175 | ffi: options.ffi, | ||
176 | output: options.output, | ||
177 | }, options.pscArgs)) | ||
178 | |||
179 | debug('spawning compiler %s %o', options.psc, args) | ||
180 | |||
181 | return (new Promise((resolve, reject) => { | ||
182 | console.log('\nCompiling PureScript...') | ||
183 | |||
184 | const compilation = spawn(options.psc, args) | ||
185 | |||
186 | compilation.stdout.on('data', data => stderr.push(data.toString())) | ||
187 | compilation.stderr.on('data', data => stderr.push(data.toString())) | ||
188 | |||
189 | compilation.on('close', code => { | ||
190 | console.log('Finished compiling PureScript.') | ||
191 | cache.compilationFinished = true | ||
192 | if (code !== 0) { | ||
193 | cache.errors = stderr.join('') | ||
194 | reject(true) | ||
195 | } else { | ||
196 | cache.warnings = stderr.join('') | ||
197 | resolve(psModule) | ||
198 | } | ||
199 | }) | ||
200 | })) | ||
201 | .then(compilerOutput => { | ||
202 | if (options.bundle) { | ||
203 | return bundle(options, cache).then(() => psModule) | ||
204 | } | ||
205 | return psModule | ||
206 | }) | ||
207 | } | ||
208 | |||
209 | function rebuild(psModule) { | ||
210 | const options = psModule.options | ||
211 | const cache = psModule.cache | ||
212 | |||
213 | debug('attempting rebuild with psc-ide-client %s', psModule.srcPath) | ||
214 | |||
215 | const request = (body) => new Promise((resolve, reject) => { | ||
216 | const args = dargs(options.pscIdeArgs) | ||
217 | const ideClient = spawn('psc-ide-client', args) | ||
218 | |||
219 | ideClient.stdout.once('data', data => { | ||
220 | let res = null | ||
221 | |||
222 | try { | ||
223 | res = JSON.parse(data.toString()) | ||
224 | debug(res) | ||
225 | } catch (err) { | ||
226 | return reject(err) | ||
227 | } | ||
228 | |||
229 | if (res && !Array.isArray(res.result)) { | ||
230 | return res.resultType === 'success' | ||
231 | ? resolve(psModule) | ||
232 | : reject('psc-ide rebuild failed') | ||
233 | } | ||
234 | |||
235 | Promise.map(res.result, (item, i) => { | ||
236 | debug(item) | ||
237 | return formatIdeResult(item, options, i, res.result.length) | ||
238 | }) | ||
239 | .then(compileMessages => { | ||
240 | if (res.resultType === 'error') { | ||
241 | if (res.result.some(item => item.errorCode === 'UnknownModule')) { | ||
242 | console.log('Unknown module, attempting full recompile') | ||
243 | return compile(psModule) | ||
244 | .then(() => request({ command: 'load' })) | ||
245 | .then(resolve) | ||
246 | .catch(() => reject('psc-ide rebuild failed')) | ||
247 | } | ||
248 | cache.errors = compileMessages.join('\n') | ||
249 | reject('psc-ide rebuild failed') | ||
250 | } else { | ||
251 | cache.warnings = compileMessages.join('\n') | ||
252 | resolve(psModule) | ||
253 | } | ||
254 | }) | ||
255 | }) | ||
256 | |||
257 | ideClient.stderr.once('data', data => reject(data.toString())) | ||
258 | |||
259 | ideClient.stdin.write(JSON.stringify(body)) | ||
260 | ideClient.stdin.write('\n') | ||
261 | }) | ||
262 | |||
263 | return request({ | ||
264 | command: 'rebuild', | ||
265 | params: { | ||
266 | file: psModule.srcPath, | ||
267 | } | ||
268 | }) | ||
269 | } | ||
270 | |||
271 | function formatIdeResult(result, options, index, length) { | ||
272 | const srcPath = path.relative(options.context, result.filename) | ||
273 | const pos = result.position | ||
274 | const fileAndPos = `${srcPath}:${pos.startLine}:${pos.startColumn}` | ||
275 | let numAndErr = `[${index+1}/${length} ${result.errorCode}]` | ||
276 | numAndErr = options.pscIdeColors ? colors.yellow(numAndErr) : numAndErr | ||
277 | |||
278 | return fs.readFileAsync(result.filename, 'utf8').then(source => { | ||
279 | const lines = source.split('\n').slice(pos.startLine - 1, pos.endLine) | ||
280 | const endsOnNewline = pos.endColumn === 1 && pos.startLine !== pos.endLine | ||
281 | const up = options.pscIdeColors ? colors.red('^') : '^' | ||
282 | const down = options.pscIdeColors ? colors.red('v') : 'v' | ||
283 | let trimmed = lines.slice(0) | ||
284 | |||
285 | if (endsOnNewline) { | ||
286 | lines.splice(lines.length - 1, 1) | ||
287 | pos.endLine = pos.endLine - 1 | ||
288 | pos.endColumn = lines[lines.length - 1].length || 1 | ||
289 | } | ||
290 | |||
291 | // strip newlines at the end | ||
292 | if (endsOnNewline) { | ||
293 | trimmed = lines.reverse().reduce((trimmed, line, i) => { | ||
294 | if (i === 0 && line === '') trimmed.trimming = true | ||
295 | if (!trimmed.trimming) trimmed.push(line) | ||
296 | if (trimmed.trimming && line !== '') { | ||
297 | trimmed.trimming = false | ||
298 | trimmed.push(line) | ||
299 | } | ||
300 | return trimmed | ||
301 | }, []).reverse() | ||
302 | pos.endLine = pos.endLine - (lines.length - trimmed.length) | ||
303 | pos.endColumn = trimmed[trimmed.length - 1].length || 1 | ||
304 | } | ||
305 | |||
306 | const spaces = ' '.repeat(String(pos.endLine).length) | ||
307 | let snippet = trimmed.map((line, i) => { | ||
308 | return ` ${pos.startLine + i} ${line}` | ||
309 | }).join('\n') | ||
310 | |||
311 | if (trimmed.length === 1) { | ||
312 | snippet += `\n ${spaces} ${' '.repeat(pos.startColumn - 1)}${up.repeat(pos.endColumn - pos.startColumn + 1)}` | ||
313 | } else { | ||
314 | snippet = ` ${spaces} ${' '.repeat(pos.startColumn - 1)}${down}\n${snippet}` | ||
315 | snippet += `\n ${spaces} ${' '.repeat(pos.endColumn - 1)}${up}` | ||
316 | } | ||
317 | |||
318 | return Promise.resolve( | ||
319 | `\n${numAndErr} ${fileAndPos}\n\n${snippet}\n\n${result.message}` | ||
320 | ) | ||
321 | }) | ||
322 | } | ||
323 | |||
324 | function bundle(options, cache) { | ||
325 | if (cache.bundle) return Promise.resolve(cache.bundle) | ||
326 | |||
327 | const stdout = [] | ||
328 | const stderr = cache.bundle = [] | ||
329 | |||
330 | const args = dargs(Object.assign({ | ||
331 | _: [path.join(options.output, '*', '*.js')], | ||
332 | output: options.bundleOutput, | ||
333 | namespace: options.bundleNamespace, | ||
334 | }, options.pscBundleArgs)) | ||
335 | |||
336 | cache.bundleModules.forEach(name => args.push('--module', name)) | ||
337 | |||
338 | debug('spawning bundler %s %o', options.pscBundle, args.join(' ')) | ||
339 | |||
340 | return (new Promise((resolve, reject) => { | ||
341 | console.log('Bundling PureScript...') | ||
342 | |||
343 | const compilation = spawn(options.pscBundle, args) | ||
344 | |||
345 | compilation.stdout.on('data', data => stdout.push(data.toString())) | ||
346 | compilation.stderr.on('data', data => stderr.push(data.toString())) | ||
347 | compilation.on('close', code => { | ||
348 | if (code !== 0) { | ||
349 | cache.errors = (cache.errors || '') + stderr.join('') | ||
350 | return reject(true) | ||
351 | } | ||
352 | cache.bundle = stderr | ||
353 | resolve(fs.appendFileAsync('output/bundle.js', `module.exports = ${options.bundleNamespace}`)) | ||
354 | }) | ||
355 | })) | ||
356 | } | ||
357 | |||
358 | // map of PS module names to their source path | ||
359 | function psModuleMap(options, cache) { | ||
360 | if (cache.psModuleMap) return Promise.resolve(cache.psModuleMap) | ||
361 | |||
362 | const globs = [].concat(options.src).concat(options.ffi) | ||
363 | |||
364 | return globby(globs).then(paths => { | ||
365 | return Promise | ||
366 | .props(paths.reduce((map, file) => { | ||
367 | map[file] = fs.readFileAsync(file, 'utf8') | ||
368 | return map | ||
369 | }, {})) | ||
370 | .then(fileMap => { | ||
371 | cache.psModuleMap = Object.keys(fileMap).reduce((map, file) => { | ||
372 | const source = fileMap[file] | ||
373 | const ext = path.extname(file) | ||
374 | const isPurs = ext.match(/purs$/i) | ||
375 | const moduleRegex = isPurs ? srcModuleRegex : ffiModuleRegex | ||
376 | const moduleName = match(moduleRegex, source) | ||
377 | map[moduleName] = map[moduleName] || {} | ||
378 | if (isPurs) { | ||
379 | map[moduleName].src = path.resolve(file) | ||
380 | } else { | ||
381 | map[moduleName].ffi = path.resolve(file) | ||
382 | } | ||
383 | return map | ||
384 | }, {}) | ||
385 | return cache.psModuleMap | ||
386 | }) | ||
387 | }) | ||
388 | } | ||
389 | |||
390 | function connectIdeServer(psModule) { | ||
391 | const options = psModule.options | ||
392 | const cache = psModule.cache | ||
393 | |||
394 | if (cache.ideServer) return Promise.resolve(psModule) | ||
395 | |||
396 | cache.ideServer = true | ||
397 | |||
398 | const connect = () => new Promise((resolve, reject) => { | ||
399 | const args = dargs(options.pscIdeArgs) | ||
400 | |||
401 | debug('attempting to connect to psc-ide-server', args) | ||
402 | |||
403 | const ideClient = spawn('psc-ide-client', args) | ||
404 | |||
405 | ideClient.stderr.on('data', data => { | ||
406 | debug(data.toString()) | ||
407 | cache.ideServer = false | ||
408 | reject(true) | ||
409 | }) | ||
410 | ideClient.stdout.once('data', data => { | ||
411 | debug(data.toString()) | ||
412 | if (data.toString()[0] === '{') { | ||
413 | const res = JSON.parse(data.toString()) | ||
414 | if (res.resultType === 'success') { | ||
415 | cache.ideServer = ideServer | ||
416 | resolve(psModule) | ||
417 | } else { | ||
418 | cache.ideServer = ideServer | ||
419 | reject(true) | ||
420 | } | ||
421 | } else { | ||
422 | cache.ideServer = false | ||
423 | reject(true) | ||
424 | } | ||
425 | }) | ||
426 | ideClient.stdin.resume() | ||
427 | ideClient.stdin.write(JSON.stringify({ command: 'load' })) | ||
428 | ideClient.stdin.write('\n') | ||
429 | }) | ||
430 | |||
431 | const args = dargs(Object.assign({ | ||
432 | outputDirectory: options.output, | ||
433 | }, options.pscIdeArgs)) | ||
434 | |||
435 | debug('attempting to start psc-ide-server', args) | ||
436 | |||
437 | const ideServer = cache.ideServer = spawn('psc-ide-server', []) | ||
438 | ideServer.stderr.on('data', data => { | ||
439 | debug(data.toString()) | ||
440 | }) | ||
441 | |||
442 | return retryPromise((retry, number) => { | ||
443 | return connect().catch(error => { | ||
444 | if (!cache.ideServer && number === 9) { | ||
445 | debug(error) | ||
446 | |||
447 | console.log( | ||
448 | 'failed to connect to or start psc-ide-server, ' + | ||
449 | 'full compilation will occur on rebuild' | ||
450 | ) | ||
451 | |||
452 | return Promise.resolve(psModule) | ||
453 | } | ||
454 | |||
455 | return retry(error) | ||
456 | }) | ||
457 | }, { | ||
458 | retries: 9, | ||
459 | factor: 1, | ||
460 | minTimeout: 333, | ||
461 | maxTimeout: 333, | ||
462 | }) | ||
463 | } | ||
464 | |||
465 | function match(regex, str) { | ||
466 | const matches = str.match(regex) | ||
467 | return matches && matches[1] | ||
468 | } | ||
469 | |||
470 | function dargs(obj) { | ||
471 | return Object.keys(obj).reduce((args, key) => { | ||
472 | const arg = '--' + key.replace(/[A-Z]/g, '-$&').toLowerCase(); | ||
473 | const val = obj[key] | ||
474 | |||
475 | if (key === '_') val.forEach(v => args.push(v)) | ||
476 | else if (Array.isArray(val)) val.forEach(v => args.push(arg, v)) | ||
477 | else args.push(arg, obj[key]) | ||
478 | |||
479 | return args.filter(arg => (typeof arg !== 'boolean')) | ||
480 | }, []) | ||
481 | } | ||
diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index a39832f..0000000 --- a/webpack.config.js +++ /dev/null | |||
@@ -1,32 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | var path = require('path'); | ||
4 | |||
5 | var webpack = require('webpack'); | ||
6 | |||
7 | var packageJson = require('./package.json'); | ||
8 | |||
9 | var noErrorsPlugin = webpack.NoErrorsPlugin; | ||
10 | |||
11 | var dedupePlugin = webpack.optimize.DedupePlugin; | ||
12 | |||
13 | var config | ||
14 | = { cache: true | ||
15 | , target: 'node' | ||
16 | , entry: { index: './entry' } | ||
17 | , externals: Object.keys(packageJson.dependencies).reduce(function(b, a){ | ||
18 | b[a] = 'commonjs ' + a; | ||
19 | return b; | ||
20 | }, {}) | ||
21 | , output: { path: __dirname | ||
22 | , filename: '[name].js' | ||
23 | , libraryTarget: 'commonjs2' | ||
24 | } | ||
25 | , plugins: [ new noErrorsPlugin() | ||
26 | , new dedupePlugin() | ||
27 | ] | ||
28 | , resolve: { modulesDirectories: [ 'build' ] } | ||
29 | } | ||
30 | ; | ||
31 | |||
32 | module.exports = config; | ||