Toggles `--no-prefix` that does not include the comment header.
+###### `requirePath` (String)
+
+Sets `--require-path=<string>` that specifies the path prefix to use for `require()` calls in the generated JavaScript.
+
+###### `ffi` (String Array)
+
+Specifies the PureScript FFI files setting `--ffi=<string>`. Glob syntax is supported. This option is specified as `ffi[]=path`.
+
###### `src` (String Array)
-Specifies PureScript source paths to be globbed for `.purs` files. By default, `bower_components` is search. Additional paths may be specified using this option. This option is specified as `src[]=path`.
+Specifies the PureScript source files. Glob syntax is supported. This option is specified as `src[]=path`.
## Example
+```js
+// webpack.config.js
+
+var path = require('path');
+
+var srcs = ['src[]=bower_components/purescript-*/src/**/*.purs', 'src[]=src/**/*.purs'];
+
+var ffis = ['ffi[]=bower_components/purescript-*/src/**/*.js'];
+
+var output = 'output';
+
+var modulesDirectories = [
+ 'node_modules',
+ // The bower component for purescript-prelude is specified here to
+ // allow JavaScript files to require the 'Prelude' module globally.
+ 'bower_components/purescript-prelude/src',
+ // The output directory is specified here to allow PureScript files in
+ // your source to import other PureScript modules in your source.
+ output
+];
+
+var config
+ = { entry: './src/entry'
+ , output: { path: __dirname
+ , pathinfo: true
+ , filename: 'bundle.js'
+ }
+ , module: { loaders: [ { test: /\.purs$/
+ , loader: 'purs-loader?output=' + output + '&' + srcs.concat(ffis).join('&')
+ } ] }
+ , resolve: { modulesDirectories: modulesDirectories
+ , extensions: ['', '.js', '.purs']
+ }
+ , resolveLoader: { root: path.join(__dirname, 'node_modules') }
+ }
+ ;
+
+module.exports = config;
+```
+
See the [example](https://github.com/ethul/purs-loader/tree/master/example) directory for a complete example.
```bash
bower install
npm install
-npm run-script webpack
+npm run-script build
npm run-script run
```
"name": "example",
"private": true,
"devDependencies": {
- "purescript-maybe": "~0.2.1"
+ "purescript-prelude": "~0.1.0"
}
}
"version": "0.0.0",
"private": true,
"scripts": {
- "webpack": "./node_modules/.bin/webpack",
+ "build": "./node_modules/.bin/webpack",
"run": "node bundle.js",
"clean": "rm -rf bower_components && rm -rf bundle.js && rm -rf node_modules && rm -rf output"
},
module Foo (foo) where
-import Data.Maybe
+import Prelude
+
import qualified Foo.Bar as B
foo = "b"
+
+foo' = "c"
module Foo.Bar (bar) where
bar = "c"
+
+bar' = "d"
module Test (test) where
-import Data.Maybe
+import Prelude
test = "a"
+var Prelude = require('Prelude');
+
var test = require('./Test');
var foo = require('./Foo');
var baz = require('./Foo/Baz');
-console.log(test, foo, baz);
+var bar = require('./Foo/Bar');
+
+console.log(Prelude, test, foo, baz, bar);
var path = require('path');
+var srcs = ['src[]=bower_components/purescript-*/src/**/*.purs', 'src[]=src/**/*.purs'];
+
+var ffis = ['ffi[]=bower_components/purescript-*/src/**/*.js'];
+
+var output = 'output';
+
+var modulesDirectories = [
+ 'node_modules',
+ 'bower_components/purescript-prelude/src',
+ output
+];
+
var config
= { entry: './src/entry'
, output: { path: __dirname
+ , pathinfo: true
, filename: 'bundle.js'
}
- , module: { loaders: [ { test: /\.purs$/, loader: 'purs-loader?src[]=src' } ] }
- , resolve: { modulesDirectories: [ 'node_modules',
- 'output'
- ]
+ , module: { loaders: [ { test: /\.purs$/
+ , loader: 'purs-loader?output=' + output + '&' + srcs.concat(ffis).join('&')
+ } ] }
+ , resolve: { modulesDirectories: modulesDirectories
, extensions: ['', '.js', '.purs']
}
, resolveLoader: { root: path.join(__dirname, 'node_modules') }
+++ /dev/null
-module PursLoader.FS
- ( FS()
- , readFileUtf8
- , readFileUtf8Sync
- ) where
-
-import Control.Monad.Aff (Aff(), makeAff)
-import Control.Monad.Eff (Eff())
-import Control.Monad.Eff.Exception (Error())
-
-import Data.Function
-
-foreign import data FS :: !
-
-readFileUtf8 :: forall eff. String -> Aff (fs :: FS | eff) String
-readFileUtf8 filepath = makeAff $ runFn3 readFileUtf8Fn filepath
-
-readFileUtf8Sync :: forall eff. String -> Eff (fs :: FS | eff) String
-readFileUtf8Sync filepath = readFileUtf8SyncFn filepath
-
-foreign import readFileUtf8Fn """
-function readFileUtf8Fn(filepath, errback, callback) {
- return function(){
- var fs = require('fs');
-
- fs.readFile(filepath, 'utf-8', function(e, data){
- if (e) errback(e)();
- else callback(data)();
- });
- };
-}
-""" :: forall eff. Fn3 String
- (Error -> Eff (fs :: FS | eff) Unit)
- (String -> Eff (fs :: FS | eff) Unit)
- (Eff (fs :: FS | eff) Unit)
-
-foreign import readFileUtf8SyncFn """
-function readFileUtf8SyncFn(filepath) {
- return function(){
- var fs = require('fs');
-
- return fs.readFileSync(filepath, {encoding: 'utf-8'});
- };
-}
-""" :: forall eff. String -> (Eff (fs :: FS | eff) String)
+++ /dev/null
-module PursLoader.Glob
- ( Glob()
- , glob
- ) where
-
-import Control.Monad.Aff (Aff(), makeAff)
-import Control.Monad.Eff (Eff())
-import Control.Monad.Eff.Exception (Error())
-
-import Data.Function
-
-foreign import data Glob :: !
-
-glob :: forall eff. String -> Aff (glob :: Glob | eff) [String]
-glob pattern = makeAff $ runFn3 globFn pattern
-
-foreign import globFn """
-function globFn(pattern, errback, callback) {
- return function(){
- var glob = require('glob');
-
- glob(pattern, function(e, data){
- if (e) errback(e)();
- else callback(data)();
- });
- };
-}
-""" :: forall eff. Fn3 String
- (Error -> Eff (glob :: Glob | eff) Unit)
- ([String] -> Eff (glob :: Glob | eff) Unit)
- (Eff (glob :: Glob | eff) Unit)
module PursLoader.Loader
- ( LoaderEff()
+ ( Effects()
, loader
, loaderFn
) where
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Eff.Exception (error)
-import Data.Array ((!!), catMaybes, concat, filter, null)
-import Data.Foldable (foldl)
+import Data.Array ((!!))
import Data.Function (Fn2(), mkFn2)
import Data.Maybe (Maybe(..), fromMaybe, maybe)
-import Data.Set (Set(), empty, insert, member, toList, unions)
-import Data.String (joinWith, split)
-import Data.String.Regex (Regex(), match, noFlags, regex)
-import Data.StrMap (StrMap(), fromList, lookup)
-import Data.Traversable (sequence)
-import Data.Tuple.Nested (tuple2)
+import Data.String.Regex (match, noFlags, regex)
import PursLoader.ChildProcess (ChildProcess(), spawn)
-import PursLoader.FS (FS(), readFileUtf8, readFileUtf8Sync)
-import PursLoader.Glob (Glob(), glob)
-import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, clearDependencies, addDependency, query, resourcePath)
+import PursLoader.LoaderRef (LoaderRef(), Loader(), async, cacheable, query)
import PursLoader.LoaderUtil (getRemainingRequest, parseQuery)
-import PursLoader.OS (eol)
-import PursLoader.Options (loaderSrcOption, pscMakeOptions, pscMakeDefaultOutput, pscMakeOutputOption)
-import PursLoader.Path (dirname, join, relative, resolve)
+import PursLoader.Options (loaderSrcOption, pscOptions)
-foreign import cwd "var cwd = process.cwd();" :: String
+type Effects eff = (loader :: Loader, cp :: ChildProcess | eff)
moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true }
-importRegex = regex "^\\s*import\\s+(?:qualified\\s+)?([\\w\\.]+)" noFlags { ignoreCase = true }
-
-bowerPattern = join [ "bower_components", "purescript-*", "src" ]
-
-pscMakeCommand = "psc-make"
-
-indexFilename = "index.js"
+pscCommand = "psc"
(!!!) = flip (!!)
-pursPattern :: [String] -> String
-pursPattern srcs = join [ "{" ++ joinWith "," ([ bowerPattern ] <> srcs) ++ "}"
- , "**"
- , "*.purs"
- ]
-
-type GraphModule = { file :: String, imports :: [String] }
-
-type Graph = StrMap GraphModule
-
-mkGraph :: forall eff. [String] -> Eff (fs :: FS | eff) Graph
-mkGraph files = (fromList <<< catMaybes) <$> sequence (parse <$> files)
- where parse file = do source <- readFileUtf8Sync file
- let key = match moduleRegex source >>= (!!!) 1
- lines = split eol source
- imports = catMaybes $ (\a -> match importRegex a >>= (!!!) 1) <$> lines
- return $ (\a -> tuple2 a { file: file, imports: imports }) <$> key
-
-mkDeps :: forall eff. String -> Graph -> [String]
-mkDeps key graph = toList $ go empty key
- where
- go :: Set String -> String -> Set String
- go acc key =
- let node = fromMaybe {file: "", imports: []} (lookup key graph)
- uniq = filter (not <<< flip member acc) node.imports
- acc' = foldl (flip insert) acc node.imports
- in if null uniq
- then acc'
- else unions $ go acc' <$> uniq
-
-addDeps :: forall eff. LoaderRef -> Graph -> [String] -> Eff (loader :: Loader | eff) Unit
-addDeps ref graph deps = const unit <$> (sequence $ add <$> deps)
- where add dep = let res = lookup dep graph
- path = (\a -> resolve a.file) <$> res
- in maybe (pure unit) (addDependency ref) path
-
-type LoaderAff eff a = Aff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
-
-loader' :: forall eff. LoaderRef -> String -> LoaderAff eff (Maybe String)
+loader' :: forall eff. LoaderRef -> String -> Aff (Effects eff) (Maybe String)
loader' ref source = do
liftEff $ cacheable ref
let request = getRemainingRequest ref
parsed = parseQuery $ query ref
- srcs = loaderSrcOption parsed
- opts = pscMakeOptions parsed
- pattern = pursPattern $ fromMaybe [] srcs
- key = match moduleRegex source >>= (!!!) 1
-
- files <- glob pattern
- graph <- liftEff $ mkGraph files
-
- let deps = fromMaybe [] $ flip mkDeps graph <$> key
- outputPath = fromMaybe pscMakeDefaultOutput $ pscMakeOutputOption parsed
- indexPath = (\a -> join [ outputPath, a, indexFilename ]) <$> key
-
- liftEff $ clearDependencies ref
- liftEff $ addDependency ref (resourcePath ref)
- liftEff $ addDeps ref graph deps
-
- spawn pscMakeCommand (opts <> files)
- indexFile <- sequence $ readFileUtf8 <$> indexPath
- return indexFile
+ srcs = fromMaybe [] (loaderSrcOption parsed)
+ opts = pscOptions parsed
+ moduleName = match moduleRegex source >>= (!!!) 1
+ result = (\a -> "module.exports = require('" ++ a ++ "');") <$> moduleName
-type LoaderEff eff a = Eff (loader :: Loader, glob :: Glob, cp :: ChildProcess, fs :: FS | eff) a
+ spawn pscCommand (srcs <> opts)
+ return result
-loader :: forall eff. LoaderRef -> String -> LoaderEff eff Unit
+loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit
loader ref source = do
callback <- async ref
runAff (\e -> callback (Just e) "")
- (maybe (callback (Just $ error "Loader has failed to run") "")
+ (maybe (callback (Just (error "Loader has failed to run")) "")
(callback Nothing))
(loader' ref source)
-loaderFn :: forall eff. Fn2 LoaderRef String (LoaderEff eff Unit)
+loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit)
loaderFn = mkFn2 loader
, Loader()
, async
, cacheable
- , clearDependencies
- , resourcePath
- , addDependency
, query
) where
import Control.Monad.Eff (Eff())
import Control.Monad.Eff.Exception (Error())
-import Data.Foreign (Foreign())
import Data.Function (Fn3(), runFn3)
import Data.Maybe (Maybe(), fromMaybe, isJust)
};
}""" :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
-foreign import clearDependencies """
-function clearDependencies(ref){
- return function(){
- return ref.clearDependencies();
- };
-}""" :: forall eff. LoaderRef -> Eff (loader :: Loader | eff) Unit
-
-foreign import resourcePath """
-function resourcePath(ref){
- return ref.resourcePath;
-}""" :: LoaderRef -> String
-
-foreign import addDependency """
-function addDependency(ref){
- return function(dep){
- return function(){
- return ref.addDependency(dep);
- };
- };
-}""" :: forall eff. LoaderRef -> String -> Eff (loader :: Loader | eff) Unit
-
foreign import query """
function query(ref){
return ref.query;
+++ /dev/null
-module PursLoader.OS (eol) where
-
-foreign import eol "var eol = require('os').EOL;" :: String
module PursLoader.Options
- ( pscMakeOptions
- , pscMakeDefaultOutput
- , pscMakeOutputOption
+ ( pscOptions
, loaderSrcOption
) where
+import Data.Array (concat)
import Data.Either (either)
import Data.Foreign (Foreign(), F())
import Data.Foreign.Class (IsForeign, read, readProp)
-import Data.Foreign.NullOrUndefined (NullOrUndefined(), runNullOrUndefined)
+import Data.Foreign.NullOrUndefined (NullOrUndefined(..), runNullOrUndefined)
-import Data.Maybe (Maybe(..), maybe)
+import Data.Maybe (Maybe(..), maybe, fromMaybe)
noPreludeOpt = "no-prelude"
noPrefixOpt = "no-prefix"
+requirePathOpt = "require-path"
+
srcOpt = "src"
-pscMakeDefaultOutput = "output"
+ffiOpt = "ffi"
newtype Options
= Options { noPrelude :: NullOrUndefined Boolean
, comments :: NullOrUndefined Boolean
, output :: NullOrUndefined String
, noPrefix :: NullOrUndefined Boolean
+ , requirePath :: NullOrUndefined String
, src :: NullOrUndefined [String]
+ , ffi :: NullOrUndefined [String]
}
instance isForeignOptions :: IsForeign Options where
, comments: _
, output: _
, noPrefix: _
+ , requirePath: _
, src: _
+ , ffi: _
} <$> readProp noPreludeOpt obj
<*> readProp noOptsOpt obj
<*> readProp noMagicDoOpt obj
<*> readProp commentsOpt obj
<*> readProp outputOpt obj
<*> readProp noPrefixOpt obj
- <*> readProp srcOpt obj)
+ <*> readProp requirePathOpt obj
+ <*> readProp srcOpt obj
+ <*> readProp ffiOpt obj)
class LoaderOption a where
opt :: String -> NullOrUndefined a -> [String]
instance booleanLoaderOption :: LoaderOption Boolean where
- opt key opt = maybe [] (\a -> if a then ["--" ++ key] else [])
- (runNullOrUndefined opt)
+ opt key val = maybe [] (\a -> if a then ["--" ++ key] else []) (runNullOrUndefined val)
instance stringLoaderOption :: LoaderOption String where
- opt key opt = maybe [] (\a -> ["--" ++ key ++ "=" ++ a])
- (runNullOrUndefined opt)
+ opt key val = maybe [] (\a -> ["--" ++ key ++ "=" ++ a]) (runNullOrUndefined val)
-pscMakeOutputOption :: Foreign -> Maybe String
-pscMakeOutputOption query = either (const Nothing)
- (\(Options a) -> runNullOrUndefined a.output)
- (read query)
+instance arrayLoaderOption :: (LoaderOption a) => LoaderOption [a] where
+ opt key val = concat (opt key <$> (NullOrUndefined <<< Just)
+ <$> (fromMaybe [] (runNullOrUndefined val)))
-pscMakeOptions :: Foreign -> [String]
-pscMakeOptions query = either (const []) fold parsed
+pscOptions :: Foreign -> [String]
+pscOptions query = either (const []) fold parsed
where parsed = read query :: F Options
fold (Options a) = opt noPreludeOpt a.noPrelude <>
opt noOptsOpt a.noOpts <>
opt verboseErrorsOpt a.verboseErrors <>
opt commentsOpt a.comments <>
opt outputOpt a.output <>
- opt noPrefixOpt a.noPrefix
+ opt noPrefixOpt a.noPrefix <>
+ opt requirePathOpt a.requirePath <>
+ opt ffiOpt a.ffi
loaderSrcOption :: Foreign -> Maybe [String]
-loaderSrcOption query = either (const Nothing)
- (\(Options a) -> runNullOrUndefined a.src)
- (read query)
+loaderSrcOption query = either (const Nothing) (\(Options a) -> runNullOrUndefined a.src) (read query)
+++ /dev/null
-module PursLoader.Path
- ( dirname
- , join
- , relative
- , resolve
- ) where
-
-foreign import dirname """
-function dirname(filepath) {
- var path = require('path');
- return path.dirname(filepath);
-}
-""" :: String -> String
-
-foreign import join """
-function join(parts) {
- var path = require('path');
- return path.join.apply(path, parts);
-}
-""" :: [String] -> String
-
-foreign import relative """
-function relative(from) {
- return function(to){
- var path = require('path');
- return path.relative(from, to);
- };
-}
-""" :: String -> String -> String
-
-foreign import resolve """
-function resolve(filepath) {
- var path = require('path');
- return path.resolve(filepath);
-}
-""" :: String -> String