diff --git a/MODULE.md b/MODULE.md index 9bd324d..27f5351 100644 --- a/MODULE.md +++ b/MODULE.md @@ -140,6 +140,47 @@ multipipe2 :: forall a b c. Stream a b -> Stream b c -> Stream a c +## Module GulpPurescript.OS + +#### `OS` + +``` purescript +data OS :: ! +``` + + +#### `Platform` + +``` purescript +data Platform + = Darwin + | Linux + | Win32 +``` + + +#### `showPlatform` + +``` purescript +instance showPlatform :: Show Platform +``` + + +#### `isForeignPlatform` + +``` purescript +instance isForeignPlatform :: IsForeign Platform +``` + + +#### `platform` + +``` purescript +platform :: forall eff. Eff (os :: OS | eff) (Maybe Platform) +``` + + + ## Module GulpPurescript.Options #### `isForeignEither` @@ -156,10 +197,10 @@ instance isForeignPsc :: IsForeign Psc ``` -#### `isForeignPscMake` +#### `isForeignPscBundle` ``` purescript -instance isForeignPscMake :: IsForeign PscMake +instance isForeignPscBundle :: IsForeign PscBundle ``` @@ -170,6 +211,27 @@ instance isForeignPscDocs :: IsForeign PscDocs ``` +#### `isForeignDotPsci` + +``` purescript +instance isForeignDotPsci :: IsForeign DotPsci +``` + + +#### `isForeignPathArray` + +``` purescript +instance isForeignPathArray :: IsForeign PathArray +``` + + +#### `isForeignDocgen` + +``` purescript +instance isForeignDocgen :: IsForeign Docgen +``` + + #### `isForeignFormat` ``` purescript @@ -177,72 +239,73 @@ instance isForeignFormat :: IsForeign Format ``` -#### `pscOptions` +#### `commandLineOptionBoolean` ``` purescript -pscOptions :: Foreign -> [String] +instance commandLineOptionBoolean :: CommandLineOption Boolean ``` -#### `pscOptionsNoOutput` +#### `commandLineOptionString` ``` purescript -pscOptionsNoOutput :: Foreign -> Tuple (Maybe String) [String] +instance commandLineOptionString :: CommandLineOption String ``` -#### `pscMakeOptions` +#### `commandLineOptionEither` ``` purescript -pscMakeOptions :: Foreign -> [String] +instance commandLineOptionEither :: (CommandLineOption a, CommandLineOption b) => CommandLineOption (Either a b) ``` -#### `pscDocsOptions` +#### `commandLineOptionArray` ``` purescript -pscDocsOptions :: Foreign -> [String] +instance commandLineOptionArray :: (CommandLineOption a) => CommandLineOption [a] ``` +#### `commandLineOptionPathArray` -## Module GulpPurescript.OS +``` purescript +instance commandLineOptionPathArray :: CommandLineOption PathArray +``` -#### `OS` + +#### `commandLineOptionDocgen` ``` purescript -data OS :: ! +instance commandLineOptionDocgen :: CommandLineOption Docgen ``` -#### `Platform` +#### `commandLineOptionFormat` ``` purescript -data Platform - = Darwin - | Linux - | Win32 +instance commandLineOptionFormat :: CommandLineOption Format ``` -#### `showPlatform` +#### `pscOptions` ``` purescript -instance showPlatform :: Show Platform +pscOptions :: Foreign -> Either ForeignError [String] ``` -#### `isForeignPlatform` +#### `pscBundleOptions` ``` purescript -instance isForeignPlatform :: IsForeign Platform +pscBundleOptions :: Foreign -> Either ForeignError [String] ``` -#### `platform` +#### `pscDocsOptions` ``` purescript -platform :: forall eff. Eff (os :: OS | eff) (Maybe Platform) +pscDocsOptions :: Foreign -> Either ForeignError [String] ``` @@ -308,28 +371,28 @@ type Effects eff = (which :: Which, through2 :: Through2, resolveBin :: ResolveB #### `psc` ``` purescript -psc :: forall eff. Foreign -> Eff (Effects eff) (Stream File File) +psc :: forall eff. Foreign -> (Error -> Eff (Effects eff) Unit) -> (Unit -> Eff (Effects eff) Unit) -> Eff (Effects eff) Unit ``` -#### `pscMake` +#### `pscBundle` ``` purescript -pscMake :: forall eff. Foreign -> Eff (Effects eff) (Stream File Unit) +pscBundle :: forall eff. Foreign -> (Error -> Eff (Effects eff) Unit) -> (Unit -> Eff (Effects eff) Unit) -> Eff (Effects eff) Unit ``` #### `pscDocs` ``` purescript -pscDocs :: forall eff. Foreign -> Eff (Effects eff) (Stream File File) +pscDocs :: forall eff. Foreign -> (Error -> Eff (Effects eff) Unit) -> (File -> Eff (Effects eff) Unit) -> Eff (Effects eff) Unit ``` -#### `dotPsci` +#### `psci` ``` purescript -dotPsci :: forall eff. Eff (Effects eff) (Stream File Unit) +psci :: forall eff. Eff (Effects eff) (Stream File Unit) ``` diff --git a/README.md b/README.md index b8b9af2..cb2bb23 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,10 @@ var gulp = require('gulp'); var purescript = require('gulp-purescript'); -gulp.task('purescript', function(){ - return gulp.src('src/**/*.purs'). - pipe(purescript.psc({noPrelude: true})). - pipe(gulp.dest('build')); +gulp.task('psc', function(){ + return purescript.psc({ + src: 'src/*.purs' + }); }); ``` @@ -36,9 +36,13 @@ Refer to the PureScript [compiler usage](https://github.com/purescript/purescrip Invokes the `psc` command. The following options are supported. -###### `noPrelude` (Boolean) +###### `src` (String or String Array) -Toggles `--no-prelude` that omits the Prelude. +Files to compile. Glob syntax is supported. + +###### `ffi` (String or String Array) + +Files for code that is included with a `foreign import` in the PureScript source. Glob syntax is supported. ###### `noTco` (Boolean) @@ -48,10 +52,6 @@ Toggles `--no-tco` that disables tail-call optimizations. Toggles `--no-magic-do` that disables optimizations overloading the do keyword generating efficient code for the `Eff` monad. -###### `main` (Boolean or String) - -Toggles `--main` or sets `--main=` that generates code to run the `main` function in the specified module or the `Main` module by default. - ###### `noOpts` (Boolean) Toggles `--no-opts` that skips the optimization phase. @@ -64,77 +64,45 @@ Toggles `--verbose-errors` that displays verbose error messages. Toggles `--comments` that includes comments in generated code. -###### `browserNamespace` (String) - -Sets `--browser-namespace=` that specifies the namespace that PureScript modules will be exported to when running in the browser. - -###### `externs` (String) - -Sets `--externs=` that specifies the externs file. - -###### `module` (String Array) - -Sets one or more `--module=` that enables dead code elimination, removing all code without a transitive dependency of one of the specified modules. - -###### `codegen` (String Array) - -Sets one or more `--codegen=` that generates code and externs for the specified modules. - ###### `output` (String) -Sets the path value of the [File](https://github.com/wearefractal/vinyl) passed through the Gulp stream. Note that this will not set `--output=`. +Sets `--output=` the specifies the output directory, `output` by default. ###### `noPrefix` (Boolean) Toggles `--no-prefix` that does not include the comment header. -###### `ffi` (String Array) - -Sets one or more `--ffi=` that specifies the files for code that is included with a `foreign import` in the PureScript source. - -### `purescript.pscMake(options)` - -Invokes the `psc-make` command. The following options are supported. - -###### `noPrelude` (Boolean) - -Toggles `--no-prelude` that omits the Prelude. - -###### `noTco` (Boolean) - -Toggles `--no-tco` that disables tail-call optimizations. +### `purescript.pscBundle(options)` -###### `noMagicDo` (Boolean) +Invokes the `psc-bundle` command. The following options are supported. -Toggles `--no-magic-do` that disables optimizations overloading the do keyword generating efficient code for the `Eff` monad. +###### `src` (String or String Array) -###### `noOpts` (Boolean) +The `psc`-produced JavaScript source files to bundle. Glob syntax is supported. -Toggles `--no-opts` that skips the optimization phase. - -###### `verboseErrors` (Boolean) +###### `output` (String) -Toggles `--verbose-errors` that displays verbose error messages. +Sets `--output=` that specifies the output filename for the bundle. -###### `comments` (Boolean) +###### `module` (String or String Array) -Toggles `--comments` that includes comments in generated code. +The name of the module or modules to use as entry points for dead code elimination. -###### `output` (String) +###### `main` (Boolean or String) -Sets `--output=` the specifies the output directory, `output` by default. +Toggles `--main` or sets `--main=` that generates code to run the `main` function in the specified module or the `Main` module by default. -###### `noPrefix` (Boolean) +###### `browserNamespace` (String) -Toggles `--no-prefix` that does not include the comment header. +Sets `--browser-namespace=` that specifies the namespace that PureScript modules will be exported to when running in the browser. -###### `ffi` (String Array) +### `purescript.pscDocs(options)` -Sets one or more `--ffi=` that specifies files for code that is included with a `foreign import` in the PureScript source. +Invokes the `psc-docs` command. The following options are supported. -### `purescript.pscDocs(options)` +###### `src` (String or String Array) -Invokes the `pscDocs` command. The following options are supported. +Files to be used for generating the documentation. Glob syntax is supported. ###### `format` (markdown | etags | ctags) @@ -146,11 +114,19 @@ Sets `--docgen=...` that can be used to filter the modules documentation is gene - If a string value is provided, the documentation for that single module will be generated. - If a list of strings is provided, the documentation for all listed modules will be generated. -- If an object with module name/filename pairs (for example, `{ Module: "docs/Module.md" }`) is provided, files will be written for each of the modules. In this mode, the task requires no `dest` as no value is returned. +- If an object with module name/filename pairs (for example, `{ Module: 'docs/Module.md' }`) is provided, files will be written for each of the modules. In this mode, the task requires no `dest` as no value is returned. + +### `purescript.psci(options)` + +Generates a `.psci` file. + +###### `src` (String or String Array) + +Files added to the `.psci` file with the `:m` command. Glob syntax is supported. -### `purescript.dotPsci()` +###### `ffi` (String or String Array) -Generates a `.psci` file in the current directory. Each source file is added with the `:m` command. +Files added to the `.psci` file with the `:f` command. Glob syntax is supported. ## Command line arguments diff --git a/entry.js b/entry.js index edf7fec..1efd5d6 100644 --- a/entry.js +++ b/entry.js @@ -3,29 +3,25 @@ var gulpPurescript = require('GulpPurescript.Plugin'); function psc(options) { - var result = gulpPurescript.psc(options); - return result(); + return gulpPurescript.psc(options)(); } -function pscMake(options) { - var result = gulpPurescript.pscMake(options); - return result(); +function pscBundle(options) { + return gulpPurescript.pscBundle(options)(); } function pscDocs(options) { - var result = gulpPurescript.pscDocs(options); - return result(); + return gulpPurescript.pscDocs(options)(); } -function dotPsci() { - var result = gulpPurescript.dotPsci(); - return result; +function psci(options) { + return gulpPurescript.psci(options)(); } module.exports.psc = psc; -module.exports.pscMake = pscMake; +module.exports.pscBundle = pscBundle; module.exports.pscDocs = pscDocs; -module.exports.dotPsci = dotPsci; +module.exports.psci = psci; diff --git a/foreign.js b/foreign.js new file mode 100644 index 0000000..40e218c --- /dev/null +++ b/foreign.js @@ -0,0 +1,2 @@ +function test(){ +} diff --git a/package.json b/package.json index 6cd810c..182ea23 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "gulp-purescript", "description": "Run the PureScript compiler", - "version": "0.5.0-rc.1", + "version": "0.5.0-rc.2", "license": "MIT", "repository": "purescript-contrib/gulp-purescript", "author": { @@ -29,14 +29,13 @@ "purescript" ], "dependencies": { + "async": "^1.3.0", "cross-spawn": "^0.4.0", "glob": "^5.0.5", "gulp-util": "^3.0.4", "logalot": "^2.1.0", "minimist": "^1.1.1", - "multipipe": "^0.1.2", "resolve-bin": "^0.3.0", - "through2": "^0.6.3", "which": "^1.0.9" }, "devDependencies": { @@ -48,6 +47,7 @@ "run-sequence": "^1.0.2", "tap-spec": "^2.2.2", "tape": "^3.5.0", + "through2": "^0.6.3", "webpack": "^1.8.9" } } diff --git a/src/FS.purs b/src/FS.purs deleted file mode 100644 index 591606b..0000000 --- a/src/FS.purs +++ /dev/null @@ -1,20 +0,0 @@ -module GulpPurescript.FS - ( FS() - , Stream() - , createWriteStream - ) where - -import Control.Monad.Eff (Eff()) - -foreign import data FS :: ! - -data Stream i o - -foreign import createWriteStream """ -function createWriteStream(path) { - return function(){ - var fs = require('fs'); - return fs.createWriteStream(path); - }; -} -""" :: forall eff. String -> Eff (fs :: FS | eff) (Stream String Unit) diff --git a/src/Glob.purs b/src/Glob.purs new file mode 100644 index 0000000..64190cf --- /dev/null +++ b/src/Glob.purs @@ -0,0 +1,53 @@ +module GulpPurescript.Glob + ( Glob() + , glob + , globAll + ) 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(error, result){ + if (error) errback(new Error(error))(); + else callback(result)(); + }); + }; +} +""" :: forall eff. Fn3 String + (Error -> Eff (glob :: Glob | eff) Unit) + ([String] -> Eff (glob :: Glob | eff) Unit) + (Eff (glob :: Glob | eff) Unit) + +globAll :: forall eff. [String] -> Aff (glob :: Glob | eff) [[String]] +globAll patterns = makeAff $ runFn3 globAllFn patterns + +foreign import globAllFn """ +function globAllFn(patterns, errback, callback) { + return function(){ + var glob = require('glob'); + + var async = require('async'); + + async.map(patterns, glob, function(error, result){ + if (error) errback(new Error(error))(); + else callback(result)(); + }); + }; +} +""" :: forall eff. Fn3 [String] + (Error -> Eff (glob :: Glob | eff) Unit) + ([[String]] -> Eff (glob :: Glob | eff) Unit) + (Eff (glob :: Glob | eff) Unit) diff --git a/src/GulpUtil.purs b/src/GulpUtil.purs index c3bdee7..6d527fb 100644 --- a/src/GulpUtil.purs +++ b/src/GulpUtil.purs @@ -2,9 +2,6 @@ module GulpPurescript.GulpUtil ( File() , mkPluginError , mkFile - , filePath - , fileIsNull - , fileIsStream ) where import Control.Monad.Eff.Exception (Error()) @@ -34,21 +31,3 @@ function mkFileFn(path, contents) { return new gutil.File({path: path, contents: contents}); } """ :: Fn2 String Buffer File - -foreign import filePath """ -function filePath(file) { - return file.path; -} -""" :: File -> String - -foreign import fileIsNull""" -function fileIsNull(file) { - return file.isNull(); -} -""" :: File -> Boolean - -foreign import fileIsStream """ -function fileIsStream(file) { - return file.isStream(); -} -""" :: File -> Boolean diff --git a/src/Multipipe.purs b/src/Multipipe.purs deleted file mode 100644 index 0c61fbf..0000000 --- a/src/Multipipe.purs +++ /dev/null @@ -1,17 +0,0 @@ -module GulpPurescript.Multipipe (multipipe2) where - -import Data.Function - -import GulpPurescript.FS (Stream()) - -foreign import multipipe2Fn """ -function multipipe2Fn(stream1, stream2) { - var multipipe = require('multipipe'); - return multipipe(stream1, stream2); -} -""" :: forall a b c. Fn2 (Stream a b) - (Stream b c) - (Stream a c) - -multipipe2 :: forall a b c. Stream a b -> Stream b c -> Stream a c -multipipe2 a b = runFn2 multipipe2Fn a b diff --git a/src/Options.purs b/src/Options.purs index 9369f97..36b3af7 100644 --- a/src/Options.purs +++ b/src/Options.purs @@ -1,7 +1,7 @@ module GulpPurescript.Options - ( pscOptions - , pscOptionsNoOutput - , pscMakeOptions + ( Psci(..) + , pscOptions + , pscBundleOptions , pscDocsOptions ) where @@ -18,9 +18,9 @@ import Data.Traversable (for) import Data.Tuple (Tuple()) import Data.Tuple.Nested (tuple2) -noPreludeOpt = "no-prelude" +srcOpt = "src" -noPreludeKey = "noPrelude" +srcKey = "src" noOptsOpt = "no-opts" @@ -62,14 +62,6 @@ moduleOpt = "module" moduleKey = moduleOpt -codegenOpt = "codegen" - -codegenKey = codegenOpt - -externsOpt = "externs" - -externsKey = externsOpt - formatOpt = "format" formatKey = formatOpt @@ -83,39 +75,36 @@ docgenOpt = "docgen" docgenKey = docgenOpt newtype Psc - = Psc { noPrelude :: NullOrUndefined Boolean + = Psc { src :: Either String [String] + , ffi :: NullOrUndefined (Either String [String]) + , output :: NullOrUndefined String , noTco :: NullOrUndefined Boolean , noMagicDo :: NullOrUndefined Boolean - , main :: NullOrUndefined (Either Boolean String) , noOpts :: NullOrUndefined Boolean , verboseErrors :: NullOrUndefined Boolean , comments :: NullOrUndefined Boolean - , browserNamespace :: NullOrUndefined String - , "module" :: NullOrUndefined [String] - , codegen :: NullOrUndefined [String] - , output :: NullOrUndefined String - , externs :: NullOrUndefined String , noPrefix :: NullOrUndefined Boolean - , ffi :: NullOrUndefined PathArray } -newtype PscMake - = PscMake { noPrelude :: NullOrUndefined Boolean - , noOpts :: NullOrUndefined Boolean - , noMagicDo :: NullOrUndefined Boolean - , noTco :: NullOrUndefined Boolean - , verboseErrors :: NullOrUndefined Boolean - , comments :: NullOrUndefined Boolean - , noPrefix :: NullOrUndefined Boolean - , output :: NullOrUndefined String - , ffi :: NullOrUndefined PathArray - } +newtype PscBundle + = PscBundle { src :: Either String [String] + , output :: NullOrUndefined String + , "module" :: NullOrUndefined (Either String [String]) + , main :: NullOrUndefined (Either Boolean String) + , browserNamespace :: NullOrUndefined String + } newtype PscDocs - = PscDocs { format :: NullOrUndefined Format + = PscDocs { src :: Either String [String] + , format :: NullOrUndefined Format , docgen :: NullOrUndefined Docgen } +newtype Psci + = Psci { src :: Either String [String] + , ffi :: NullOrUndefined (Either String [String]) + } + newtype Docgen = Docgen Foreign newtype PathArray = PathArray [String] @@ -128,63 +117,54 @@ instance isForeignEither :: (IsForeign a, IsForeign b) => IsForeign (Either a b) instance isForeignPsc :: IsForeign Psc where read obj = - Psc <$> ({ noPrelude: _ + Psc <$> ({ src: _ + , ffi: _ + , output: _ , noTco: _ , noMagicDo: _ - , main: _ , noOpts: _ , verboseErrors: _ , comments: _ - , browserNamespace: _ - , "module": _ - , codegen: _ - , output: _ - , externs: _ , noPrefix: _ - , ffi: _ - } <$> readProp noPreludeKey obj + } <$> readProp srcKey obj + <*> readProp ffiKey obj + <*> readProp outputKey obj <*> readProp noTcoKey obj <*> readProp noMagicDoKey obj - <*> readProp mainKey obj <*> readProp noOptsKey obj <*> readProp verboseErrorsKey obj <*> readProp commentsKey obj - <*> readProp browserNamespaceKey obj - <*> readProp moduleKey obj - <*> readProp codegenKey obj - <*> readProp outputKey obj - <*> readProp externsKey obj - <*> readProp noPrefixKey obj - <*> readProp ffiKey obj) + <*> readProp noPrefixKey obj) -instance isForeignPscMake :: IsForeign PscMake where +instance isForeignPscBundle :: IsForeign PscBundle where read obj = - PscMake <$> ({ output: _ - , noPrelude: _ - , noTco: _ - , noMagicDo: _ - , noOpts: _ - , verboseErrors: _ - , comments: _ - , noPrefix: _ - , ffi: _ - } <$> readProp outputKey obj - <*> readProp noPreludeKey obj - <*> readProp noTcoKey obj - <*> readProp noMagicDoKey obj - <*> readProp noOptsKey obj - <*> readProp verboseErrorsKey obj - <*> readProp commentsKey obj - <*> readProp noPrefixKey obj - <*> readProp ffiKey obj) + PscBundle <$> ({ src: _ + , output: _ + , "module": _ + , main: _ + , browserNamespace: _ + } <$> readProp srcKey obj + <*> readProp outputKey obj + <*> readProp moduleKey obj + <*> readProp mainKey obj + <*> readProp browserNamespaceKey obj) instance isForeignPscDocs :: IsForeign PscDocs where read obj = - PscDocs <$> ({ format: _ + PscDocs <$> ({ src: _ + , format: _ , docgen: _ - } <$> readProp formatKey obj + } <$> readProp srcKey obj + <*> readProp formatKey obj <*> readProp docgenOpt obj) +instance isForeignPsci :: IsForeign Psci where + read obj = + Psci <$> ({ src: _ + , ffi: _ + } <$> readProp srcKey obj + <*> readProp ffiKey obj) + instance isForeignPathArray :: IsForeign PathArray where read val = PathArray <$> read val @@ -217,20 +197,10 @@ instance commandLineOptionArray :: (CommandLineOption a) => CommandLineOption [a <$> (fromMaybe [] $ runNullOrUndefined val) instance commandLineOptionPathArray :: CommandLineOption PathArray where - opt key val = opt key (NullOrUndefined ((\(PathArray a) -> a >>= expandGlob) <$> (runNullOrUndefined val))) - -foreign import expandGlob - """ - var expandGlob = (function () { - var glob = require("glob"); - return function (pattern) { - return glob.sync(pattern); - }; - }()); - """ :: String -> [String] + opt key val = opt key (NullOrUndefined ((\(PathArray a) -> a >>= expandGlob) <$> (runNullOrUndefined val))) instance commandLineOptionDocgen :: CommandLineOption Docgen where - opt key val = opt key (NullOrUndefined (parseDocgen <$> (runNullOrUndefined val))) + opt key val = opt key (NullOrUndefined (parseDocgen <$> (runNullOrUndefined val))) parseDocgen :: Docgen -> [String] parseDocgen (Docgen obj) = either (const []) id $ parseName obj @@ -257,46 +227,41 @@ instance commandLineOptionFormat :: CommandLineOption Format where CTags -> NullOrUndefined (Just "ctags")) (runNullOrUndefined val)) -foldPscOptions :: Psc -> [String] -foldPscOptions (Psc a) = opt noPreludeOpt a.noPrelude <> - opt noTcoOpt a.noTco <> - opt noMagicDoOpt a.noMagicDo <> - opt mainOpt a.main <> - opt noOptsOpt a.noOpts <> - opt verboseErrorsOpt a.verboseErrors <> - opt commentsOpt a.comments <> - opt browserNamespaceOpt a.browserNamespace <> - opt moduleOpt a."module" <> - opt codegenOpt a.codegen <> - opt outputOpt a.output <> - opt externsOpt a.externs <> - opt noPrefixOpt a.noPrefix <> - opt ffiOpt a.ffi - pscOptions :: Foreign -> Either ForeignError [String] -pscOptions opts = foldPscOptions <$> (read opts :: F Psc) - -pscOptionsNoOutput :: Foreign -> Either ForeignError (Tuple (Maybe String) [String]) -pscOptionsNoOutput opts = fold <$> parsed +pscOptions opts = fold <$> parsed where parsed = read opts :: F Psc - fold (Psc a) = tuple2 (runNullOrUndefined a.output) - (foldPscOptions (Psc $ a { output = NullOrUndefined Nothing })) - -pscMakeOptions :: Foreign -> Either ForeignError [String] -pscMakeOptions opts = fold <$> parsed - where parsed = read opts :: F PscMake - fold (PscMake a) = opt outputOpt a.output <> - opt noPreludeOpt a.noPrelude <> - opt noTcoOpt a.noTco <> - opt noMagicDoOpt a.noMagicDo <> - opt noOptsOpt a.noOpts <> - opt verboseErrorsOpt a.verboseErrors <> - opt commentsOpt a.comments <> - opt noPrefixOpt a.noPrefix <> - opt ffiOpt a.ffi + fold (Psc a) = either pure id a.src <> + opt ffiOpt a.ffi <> + opt outputOpt a.output <> + opt noTcoOpt a.noTco <> + opt noMagicDoOpt a.noMagicDo <> + opt noOptsOpt a.noOpts <> + opt verboseErrorsOpt a.verboseErrors <> + opt commentsOpt a.comments <> + opt noPrefixOpt a.noPrefix + +pscBundleOptions :: Foreign -> Either ForeignError [String] +pscBundleOptions opts = fold <$> parsed + where parsed = read opts :: F PscBundle + fold (PscBundle a) = either pure id a.src <> + opt outputOpt a.output <> + opt moduleOpt a."module" <> + opt mainOpt a.main <> + opt browserNamespaceOpt a.browserNamespace pscDocsOptions :: Foreign -> Either ForeignError [String] pscDocsOptions opts = fold <$> parsed where parsed = read opts :: F PscDocs - fold (PscDocs a) = opt formatOpt a.format <> + fold (PscDocs a) = either pure id a.src <> + opt formatOpt a.format <> opt docgenOpt a.docgen + +foreign import expandGlob + """ + var expandGlob = (function () { + var glob = require("glob"); + return function (pattern) { + return glob.sync(pattern); + }; + }()); + """ :: String -> [String] diff --git a/src/Plugin.purs b/src/Plugin.purs index 48563c3..078e360 100644 --- a/src/Plugin.purs +++ b/src/Plugin.purs @@ -1,9 +1,11 @@ module GulpPurescript.Plugin ( Effects() + , Errorback() + , Callback() , psc - , pscMake + , pscBundle , pscDocs - , dotPsci + , psci ) where import Control.Monad.Aff (Aff()) @@ -12,26 +14,28 @@ import Control.Monad.Eff.Class (liftEff) import Control.Monad.Eff.Exception (Error()) import Control.Monad.Error.Class (catchError, throwError) +import Data.Array (concat) import Data.Either (Either(..), either) import Data.Foreign (Foreign()) import Data.Foreign.Class (IsForeign, read, readProp) +import Data.Foreign.NullOrUndefined (runNullOrUndefined) import Data.Maybe (Maybe(Just), maybe, fromMaybe) +import Data.String (joinWith) import Data.Tuple (Tuple(..)) import Data.Tuple.Nested (tuple2) import GulpPurescript.Buffer (Buffer(), mkBufferFromString) import GulpPurescript.ChildProcess (ChildProcess(), spawn) -import GulpPurescript.FS (FS(), Stream(), createWriteStream) -import GulpPurescript.GulpUtil (File(), fileIsNull, fileIsStream, filePath, mkFile, mkPluginError) +import GulpPurescript.Glob (Glob(), globAll) +import GulpPurescript.GulpUtil (File(), mkFile, mkPluginError) import GulpPurescript.Logalot (Logalot(), info) import GulpPurescript.Minimist (minimist) -import GulpPurescript.Multipipe (multipipe2) import GulpPurescript.OS (OS(), Platform(Win32), platform) -import GulpPurescript.Options (pscOptionsNoOutput, pscMakeOptions, pscDocsOptions) +import GulpPurescript.Options (Psci(..), pscOptions, pscBundleOptions, pscDocsOptions) import GulpPurescript.Package (Pkg(), Package(..), package) import GulpPurescript.Path (relative) import GulpPurescript.ResolveBin (ResolveBin(), resolveBin) -import GulpPurescript.Through2 (Through2(), objStream, accStream) +import GulpPurescript.Stream (Stream(), ReadableStream(), mkReadableStreamFromAff) import GulpPurescript.Which (Which(), which) newtype Argv = Argv { verbose :: Boolean } @@ -41,32 +45,36 @@ instance isForeignArgv :: IsForeign Argv where type Effects eff = ( cp :: ChildProcess - , fs :: FS + , glob :: Glob , logalot :: Logalot , os :: OS , package :: Pkg , resolveBin :: ResolveBin - , through2 :: Through2 + , stream :: Stream , which :: Which | eff ) +type Errorback eff = Error -> Eff (Effects eff) Unit + +type Callback eff a = a -> Eff (Effects eff) Unit + nodeCommand = "node" pursPackage = "purescript" psciFilename = ".psci" -psciLoadCommand = ":m" +psciLoadModuleCommand = ":m" + +psciLoadForeignCommand = ":f" pscCommand = "psc" -pscMakeCommand = "psc-make" +pscBundleCommand = "psc-bundle" pscDocsCommand = "psc-docs" -pscOutputDefault = "psc.js" - isVerbose = maybe false (\(Argv a) -> a.verbose) (minimist argv) foreign import cwd "var cwd = process.cwd();" :: String @@ -100,38 +108,44 @@ execute cmd args = do result <- spawn cmd' args' return result -pathsStream :: forall eff. Eff (through2 :: Through2 | eff) (Stream File [String]) -pathsStream = accStream run - where run i = if fileIsStream i - then throwPluginError "Streaming is not supported" - else pure $ filePath i - -psc :: forall eff. Foreign -> Eff (Effects eff) (Stream File File) -psc opts = multipipe2 <$> pathsStream <*> objStream run - where run i = case pscOptionsNoOutput opts of - Left e -> throwPluginError (show e) - Right (Tuple out opt) -> - mkFile (fromMaybe pscOutputDefault out) <$> mkBufferFromString - <$> execute pscCommand (i <> opt) - -pscMake :: forall eff. Foreign -> Eff (Effects eff) (Stream File Unit) -pscMake opts = multipipe2 <$> pathsStream <*> objStream run - where run i = do output <- either (throwPluginError <<< show) - (\a -> execute pscMakeCommand (i <> a)) - (pscMakeOptions opts) - if isVerbose - then liftEff $ info $ pscMakeCommand ++ "\n" ++ output - else pure unit - -pscDocs :: forall eff. Foreign -> Eff (Effects eff) (Stream File File) -pscDocs opts = multipipe2 <$> pathsStream <*> objStream run - where run i = case pscDocsOptions opts of - Left e -> throwPluginError (show e) - Right a-> mkFile "." <$> mkBufferFromString - <$> execute pscDocsCommand (a <> i) - -dotPsci :: forall eff. Eff (Effects eff) (Stream File Unit) -dotPsci = multipipe2 <$> objStream run <*> createWriteStream psciFilename - where run i = if fileIsStream i - then throwPluginError "Streaming is not supported" - else pure $ psciLoadCommand ++ " " ++ relative cwd (filePath i) ++ "\n" +psc :: forall eff. Foreign -> Eff (Effects eff) (ReadableStream Unit) +psc opts = mkReadableStreamFromAff $ do + output <- either (throwPluginError <<< show) + (execute pscCommand) + (pscOptions opts) + if isVerbose + then liftEff $ info $ pscCommand ++ "\n" ++ output + else pure unit + +pscBundle :: forall eff. Foreign -> Eff (Effects eff) (ReadableStream File) +pscBundle opts = mkReadableStreamFromAff (either (throwPluginError <<< show) run (pscBundleOptions opts)) + where + run :: [String] -> Aff (Effects eff) File + run args = mkFile "." <$> mkBufferFromString + <$> execute pscBundleCommand args + +pscDocs :: forall eff. Foreign -> Eff (Effects eff) (ReadableStream File) +pscDocs opts = mkReadableStreamFromAff (either (throwPluginError <<< show) run (pscDocsOptions opts)) + where + run :: [String] -> Aff (Effects eff) File + run args = mkFile "." <$> mkBufferFromString + <$> execute pscDocsCommand args + +psci :: forall eff. Foreign -> Eff (Effects eff) (ReadableStream File) +psci opts = mkReadableStreamFromAff (either (throwPluginError <<< show) run (read opts)) + where + run :: Psci -> Aff (Effects eff) File + run (Psci a) = do + srcs <- globAll (either pure id a.src) + ffis <- globAll (either pure id (fromMaybe (Right []) (runNullOrUndefined a.ffi))) + + let lines = (loadModule <$> concat srcs) <> (loadForeign <$> concat ffis) + buffer = mkBufferFromString (joinWith "\n" lines) + + return (mkFile psciFilename buffer) + + loadModule :: String -> String + loadModule a = psciLoadModuleCommand ++ " " ++ relative cwd a + + loadForeign :: String -> String + loadForeign a = psciLoadForeignCommand ++ " " ++ relative cwd a diff --git a/src/Stream.purs b/src/Stream.purs new file mode 100644 index 0000000..1e6d3a8 --- /dev/null +++ b/src/Stream.purs @@ -0,0 +1,56 @@ +module GulpPurescript.Stream + ( Stream() + , ReadableStream() + , mkReadableStreamFromAff + ) where + +import Control.Monad.Aff (Aff(), runAff) +import Control.Monad.Eff (Eff()) +import Control.Monad.Eff.Exception (Error()) + +import Data.Function + +foreign import data Stream :: ! + +data ReadableStream out + +type RunAff eff a = (Error -> Eff eff Unit) -> (a -> Eff eff Unit) -> Aff eff a -> Eff eff Unit + +mkReadableStreamFromAff :: forall eff1 eff2 out. Aff eff1 out -> Eff (stream :: Stream | eff2) (ReadableStream out) +mkReadableStreamFromAff = runFn2 mkReadableStreamFromAffFn runAff + +foreign import mkReadableStreamFromAffFn """ +function mkReadableStreamFromAffFn(runAff, aff) { + return function(){ + var stream = require('stream'); + + var objectMode = true; + + var readable = new stream.Readable({objectMode: objectMode}); + + readable._read = function(){ + }; + + function onError(e) { + return function(){ + readable.emit('error', e); + }; + } + + function onSuccess(a) { + return function(){ + readable.push(a); + readable.push(null); + }; + } + + var eff = runAff(onError)(onSuccess)(aff); + + eff(); + + return readable; + }; +} +""" :: forall eff1 eff2 out. Fn2 (RunAff eff1 out) + (Aff eff1 out) + (Eff (stream :: Stream | eff2) (ReadableStream out)) diff --git a/src/Through2.purs b/src/Through2.purs deleted file mode 100644 index 64e164c..0000000 --- a/src/Through2.purs +++ /dev/null @@ -1,96 +0,0 @@ -module GulpPurescript.Through2 - ( Through2() - , RunAff() - , objStream - , accStream - ) where - -import Control.Monad.Aff (Aff(), runAff) -import Control.Monad.Eff (Eff()) -import Control.Monad.Eff.Exception (Error()) - -import Data.Function - -import GulpPurescript.FS (Stream()) - -foreign import data Through2 :: ! - -type RunAff eff a = (Error -> Eff eff Unit) -> (a -> Eff eff Unit) -> Aff eff a -> Eff eff Unit - -objStream :: forall eff1 eff2 input output. (input -> Aff eff1 output) -> Eff (through2 :: Through2 | eff2) (Stream input output) -objStream = runFn2 objStreamFn runAff - -foreign import objStreamFn """ - function objStreamFn(runAff, aff) { - return function(){ - var through2 = require('through2'); - - function transform(chunk, encoding, callback) { - function onError(e) { - return function(){ - callback(e); - }; - } - - function onSuccess(a) { - return function(){ - callback(null, a); - }; - } - - var aff$prime = aff(chunk); - - var eff = runAff(onError)(onSuccess)(aff$prime); - - return eff(); - } - - return through2.obj(transform); - }; - } -""" :: forall eff1 eff2 input output. Fn2 (RunAff eff1 output) - (input -> Aff eff1 output) - (Eff (through2 :: Through2 | eff2) (Stream input output)) - -accStream :: forall eff1 eff2 input output. (input -> Aff eff1 output) -> Eff (through2 :: Through2 | eff2) (Stream input [output]) -accStream = runFn2 accStreamFn runAff - -foreign import accStreamFn """ - function accStreamFn(runAff, aff) { - return function(){ - var through2 = require('through2'); - - var arr = []; - - function transform(chunk, encoding, callback) { - function onError(e) { - return function(){ - callback(e); - }; - } - - function onSuccess(a) { - return function(){ - arr.push(a); - callback(); - }; - } - - var aff$prime = aff(chunk); - - var eff = runAff(onError)(onSuccess)(aff$prime); - - return eff(); - } - - function flush(callback) { - this.push(arr); - callback(); - } - - return through2.obj(transform, flush); - }; - } -""" :: forall eff1 eff2 input output. Fn2 (RunAff eff1 output) - (input -> Aff eff1 output) - (Eff (through2 :: Through2 | eff2) (Stream input [output])) diff --git a/test.js b/test.js index eb017bd..82de401 100644 --- a/test.js +++ b/test.js @@ -15,46 +15,36 @@ var rewire = require('rewire'); var purescript = require('./'); test('psc - basic', function(t){ - t.plan(2); - - var stream = purescript.psc({noPrelude: true}); + t.plan(1); - var fixture = 'Fixture1.purs'; + var purescript = rewire('./'); - gulp.src(fixture).pipe(stream). - pipe(through2.obj(function(chunk, encoding, callback){ - t.ok(/Fixture/.test(chunk.contents.toString()), 'should have a compiled result'); - t.equal('psc.js', chunk.path); - callback(); - })); -}); + var mock = { + success: function(){ + t.fail('Should not get a log message'); + } + }; -test('psc - output option', function(t){ - t.plan(2); + purescript.__set__('logalot', mock); var fixture = 'Fixture1.purs'; - var output = 'output.js'; - - var stream = purescript.psc({noPrelude: true, output: output}); + var stream = purescript.psc({src: fixture}); - gulp.src(fixture).pipe(stream). - pipe(through2.obj(function(chunk, encoding, callback){ - t.ok(!fs.existsSync(__dirname + '/' + output), 'output file should not exist'); - t.equal(output, chunk.path); + stream.pipe(through2.obj(function(chunk, encoding, callback){ + t.pass('should output a compiled result'); callback(); })); }); -test('psc - failure', function(t){ +test('psc - error', function(t){ t.plan(2); - var stream = purescript.psc({noPrelude: true}); - var fixture = 'Fixture2.purs'; - gulp.src(fixture).pipe(stream). - on('error', function(e){ + var stream = purescript.psc({src: fixture}); + + stream.on('error', function(e){ t.ok(/"where"/.test(e.message), 'should have a failure message'); t.equal('Error', e.name); }); @@ -63,82 +53,45 @@ test('psc - failure', function(t){ test('psc - invalid option type', function(t){ t.plan(2); - var fixture = 'Fixture1.purs'; - - var moduleName = path.basename(fixture, '.purs'); - - var stream = purescript.psc({noPrelude: true, module: moduleName}); - - gulp.src(fixture).pipe(stream). - on('error', function(e){ - t.ok(/type mismatch/i.test(e.message), 'should have a failure message'); - t.equal('Error', e.name); - }); + try { + var stream = purescript.psc({src: 10}); + + stream.on('error', function(e){ + t.ok(/type mismatch/i.test(error.message), 'should have a failure message'); + t.equal('Error', error.name); + }); + } + catch (error) { + t.ok(/type mismatch/i.test(error.message), 'should have a failure message'); + t.equal('Error', error.name); + } }); -test('psci - basic', function(t){ +test('psc-bundle - basic', function(t){ t.plan(1); - var stream = purescript.dotPsci(); - - var fixture = 'Fixture1.purs'; + var fixture = 'foreign.js'; - var output = ':m ' + fixture + '\n'; + var stream = purescript.pscBundle({src: fixture}); - gulp.src(fixture).pipe(stream). - pipe(through2.obj(function(chunk, encoding, callback){ - t.equal(output, chunk.toString()); + stream.pipe(through2.obj(function(chunk, encoding, callback){ + t.ok(/psc-bundle/.test(chunk.contents.toString()), 'should have a compiled result'); callback(); })); }); -test('psc-make - basic', function(t){ - t.plan(1); - - var purescript = rewire('./'); - - var mock = { - success: function(){ - t.fail('Should not get a log message'); - } - }; - - purescript.__set__('logalot', mock); - - var stream = purescript.pscMake({noPrelude: true}); - - var fixture = 'Fixture1.purs'; - - gulp.src(fixture).pipe(stream). - on('finish', function(){ - t.pass('should output a compiled result'); - }); -}); - -test('psc-make - error', function(t){ +test('psci - basic', function(t){ t.plan(2); - var stream = purescript.pscMake({noPrelude: true}); - - var fixture = 'Fixture2.purs'; - - gulp.src(fixture).pipe(stream). - on('error', function(e){ - t.ok(/"where"/.test(e.message), 'should have a failure message'); - t.equal('Error', e.name); - }); -}); - -test('psc-make - invalid option type', function(t){ - t.plan(2); + var fixture = 'Fixture1.purs'; - var stream = purescript.pscMake({noPrelude: 'invalid'}); + var output = ':m ' + fixture; - var fixture = 'Fixture1.purs'; + var stream = purescript.psci({src: fixture}); - gulp.src(fixture).pipe(stream). - on('error', function(e){ - t.ok(/type mismatch/i.test(e.message), 'should have a failure message'); - t.equal('Error', e.name); - }); + stream.pipe(through2.obj(function(chunk, encoding, callback){ + t.equal('.psci', chunk.path); + t.equal(output, chunk.contents.toString()); + callback(); + })); });