From 460393439a8dd7d94fe02a2013e96c9146282bf1 Mon Sep 17 00:00:00 2001 From: eric thul Date: Wed, 24 Feb 2016 22:53:40 -0500 Subject: Adds dependencies of modules process by the loader. The module file path and dependency graph information is provided by the purescript-webpack-plugin. Resolves #37 --- bower.json | 4 +- docs/PursLoader/Options.md | 1 + docs/PursLoader/Plugin.md | 45 ++++++++++++++++++++++ src/PursLoader/Loader.purs | 92 ++++++++++++++++++++++++++++----------------- src/PursLoader/Options.purs | 2 +- src/PursLoader/Plugin.js | 20 ++++++++++ src/PursLoader/Plugin.purs | 49 ++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 37 deletions(-) create mode 100644 docs/PursLoader/Plugin.md create mode 100644 src/PursLoader/Plugin.js create mode 100644 src/PursLoader/Plugin.purs diff --git a/bower.json b/bower.json index 8b61f6f..761c24c 100644 --- a/bower.json +++ b/bower.json @@ -3,8 +3,8 @@ "private": true, "dependencies": { "purescript-aff": "^0.13.0", - "purescript-strings": "^0.7.0", "purescript-foreign": "^0.7.0", - "purescript-unsafe-coerce": "~0.1.0" + "purescript-unsafe-coerce": "~0.1.0", + "purescript-nullable": "~0.2.1" } } diff --git a/docs/PursLoader/Options.md b/docs/PursLoader/Options.md index 2bfcddd..b3352fc 100644 --- a/docs/PursLoader/Options.md +++ b/docs/PursLoader/Options.md @@ -4,6 +4,7 @@ ``` purescript newtype Options + = Options { bundleOutput :: String } ``` ##### Instances diff --git a/docs/PursLoader/Plugin.md b/docs/PursLoader/Plugin.md new file mode 100644 index 0000000..9abec4d --- /dev/null +++ b/docs/PursLoader/Plugin.md @@ -0,0 +1,45 @@ +## Module PursLoader.Plugin + +#### `Result` + +``` purescript +type Result = { srcMap :: ImmutableMap String String, ffiMap :: ImmutableMap String String, graph :: DependencyGraph } +``` + +#### `Compile` + +``` purescript +type Compile eff = Nullable Error -> Result -> Eff eff Unit +``` + +#### `Context` + +``` purescript +type Context eff = { compile :: Compile eff -> Eff eff Unit } +``` + +#### `get` + +``` purescript +get :: forall key value. ImmutableMap key value -> key -> Maybe value +``` + +#### `dependenciesOf` + +``` purescript +dependenciesOf :: DependencyGraph -> String -> Either Error (Array String) +``` + +#### `ImmutableMap` + +``` purescript +data ImmutableMap :: * -> * -> * +``` + +#### `DependencyGraph` + +``` purescript +data DependencyGraph :: * +``` + + diff --git a/src/PursLoader/Loader.purs b/src/PursLoader/Loader.purs index affce53..f78153f 100644 --- a/src/PursLoader/Loader.purs +++ b/src/PursLoader/Loader.purs @@ -4,25 +4,27 @@ module PursLoader.Loader , loaderFn ) where -import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (<<<), (++), bind, const) +import Prelude (Unit(), ($), (>>=), (<$>), (<*>), (++), bind, const, id, pure, unit) +import Control.Apply ((*>)) import Control.Bind (join) -import Control.Monad.Eff (Eff()) +import Control.Monad.Eff (Eff(), foreachE) import Control.Monad.Eff.Exception (Error(), error) import Data.Array ((!!)) +import Data.Bifunctor (lmap) +import Data.Either (Either(..), either) +import Data.Foreign.Class (read) import Data.Function (Fn2(), mkFn2) import Data.Maybe (Maybe(..), maybe) -import Data.Either (either) -import Data.Foreign (Foreign()) -import Data.Foreign.Class (read) -import Data.Foreign.Null (runNull) +import Data.Nullable (toMaybe) import Data.String.Regex (Regex(), match, noFlags, regex) import Unsafe.Coerce (unsafeCoerce) import PursLoader.LoaderRef - ( LoaderRef() + ( AsyncCallback() + , LoaderRef() , Loader() , async , cacheable @@ -33,46 +35,68 @@ import PursLoader.LoaderRef ) import PursLoader.LoaderUtil (parseQuery) -import PursLoader.Options (runOptions) +import PursLoader.Options (Options(..)) import PursLoader.Path (dirname, relative) +import PursLoader.Plugin as Plugin type Effects eff = (loader :: Loader | eff) -type PurescriptWebpackPluginContext eff = { compile :: (Foreign -> Eff (Effects eff) Unit) -> Eff (Effects eff) Unit } - loader :: forall eff. LoaderRef -> String -> Eff (Effects eff) Unit loader ref source = do callback <- async ref cacheable ref - let parsed = parseQuery $ query ref - - options = either (const Nothing) (Just <<< runOptions) (read parsed) - - moduleName = join $ match moduleRegex source >>= \as -> as !! 1 - - resourceDir = dirname (resourcePath ref) - - modulePath = (\opts -> relative resourceDir opts.bundleOutput) <$> options - - result = (\path name -> "module.exports = require('" ++ path ++ "')['" ++ name ++ "'];") <$> modulePath <*> moduleName - - clearDependencies ref - - addDependency ref (resourcePath ref) - - pluginContext.compile (\err -> maybe (callback (Just $ error "Failed to run loader") "") - (callback (compileError err)) result) + pluginContext.compile (compile callback) where - moduleRegex :: Regex - moduleRegex = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true } - - pluginContext :: PurescriptWebpackPluginContext eff + pluginContext :: Plugin.Context (Effects eff) pluginContext = (unsafeCoerce ref).purescriptWebpackPluginContext - compileError :: Foreign -> Maybe Error - compileError value = either (const $ Just (error "Failed to compile")) ((<$>) error) (runNull <$> read value) + compile :: AsyncCallback eff -> Plugin.Compile (Effects eff) + compile callback error' { srcMap, ffiMap, graph } = do + clearDependencies ref + + addDependency ref (resourcePath ref) + + either (\err -> callback (Just err) "") id + (handle <$> name <*> dependencies <*> exports) + where + handle :: String -> Array String -> String -> Eff (Effects eff) Unit + handle name' deps res = do + addTransitive name' + foreachE deps addTransitive + callback (toMaybe error') res + + exports :: Either Error String + exports = (\a b -> "module.exports = require('" ++ a ++ "')['" ++ b ++ "'];") <$> path <*> name + + dependencies :: Either Error (Array String) + dependencies = name >>= Plugin.dependenciesOf graph + + addTransitive :: String -> Eff (Effects eff) Unit + addTransitive dep = addDep (Plugin.get srcMap dep) *> addDep (Plugin.get ffiMap dep) + where + addDep :: Maybe String -> Eff (Effects eff) Unit + addDep = maybe (pure unit) (addDependency ref) + + name :: Either Error String + name = + maybe (Left $ error "Failed to parse module name") Right + (join $ match re source >>= \as -> as !! 1) + where + re :: Regex + re = regex "(?:^|\\n)module\\s+([\\w\\.]+)" noFlags { ignoreCase = true } + + path :: Either Error String + path = (\(Options opts) -> relative resourceDir opts.bundleOutput) <$> options + where + options :: Either Error Options + options = + lmap (const $ error "Failed to parse loader query") + (read $ parseQuery (query ref)) + + resourceDir :: String + resourceDir = dirname (resourcePath ref) loaderFn :: forall eff. Fn2 LoaderRef String (Eff (Effects eff) Unit) loaderFn = mkFn2 loader diff --git a/src/PursLoader/Options.purs b/src/PursLoader/Options.purs index 706ddd2..0c1453e 100644 --- a/src/PursLoader/Options.purs +++ b/src/PursLoader/Options.purs @@ -1,5 +1,5 @@ module PursLoader.Options - ( Options() + ( Options(..) , runOptions ) where diff --git a/src/PursLoader/Plugin.js b/src/PursLoader/Plugin.js new file mode 100644 index 0000000..90feb33 --- /dev/null +++ b/src/PursLoader/Plugin.js @@ -0,0 +1,20 @@ +'use strict'; + +// module PursLoader.Plugin + +function getFn(nothing, just, map, key) { + var value = map.get(key); + return value === undefined ? nothing : just(value); +} +exports.getFn = getFn; + +function dependenciesOfFn(left, right, graph, node) { + try { + var dependencies = graph.dependenciesOf(node); + return right(dependencies); + } + catch (error) { + return left(error); + } +} +exports.dependenciesOfFn = dependenciesOfFn; diff --git a/src/PursLoader/Plugin.purs b/src/PursLoader/Plugin.purs new file mode 100644 index 0000000..23f8600 --- /dev/null +++ b/src/PursLoader/Plugin.purs @@ -0,0 +1,49 @@ +module PursLoader.Plugin + ( Result() + , Compile() + , Context() + , ImmutableMap() + , DependencyGraph() + , get + , dependenciesOf + ) where + +import Prelude (Unit()) + +import Control.Monad.Eff (Eff()) +import Control.Monad.Eff.Exception (Error()) + +import Data.Either (Either(..)) +import Data.Function (Fn4(), runFn4) +import Data.Maybe (Maybe(..)) +import Data.Nullable (Nullable()) + +type Result = { srcMap :: ImmutableMap String String, ffiMap :: ImmutableMap String String, graph :: DependencyGraph } + +type Compile eff = Nullable Error -> Result -> Eff eff Unit + +type Context eff = { compile :: Compile eff -> Eff eff Unit } + +get :: forall key value. ImmutableMap key value -> key -> Maybe value +get = runFn4 getFn Nothing Just + +dependenciesOf :: DependencyGraph -> String -> Either Error (Array String) +dependenciesOf = runFn4 dependenciesOfFn Left Right + +foreign import data ImmutableMap :: * -> * -> * + +foreign import data DependencyGraph :: * + +foreign import getFn + :: forall key value. Fn4 (Maybe value) + (value -> Maybe value) + (ImmutableMap key value) + key + (Maybe value) + +foreign import dependenciesOfFn + :: Fn4 (Error -> Either Error (Array String)) + (Array String -> Either Error (Array String)) + DependencyGraph + String + (Either Error (Array String)) -- cgit v1.2.3