packages feed

jsdom-extras 0.1.0.0 → 0.1.1.0

raw patch · 4 files changed

+163/−22 lines, 4 filesdep −jsaddle-domdep ~basedep ~jsaddledep ~lensPVP ok

version bump matches the API change (PVP)

Dependencies removed: jsaddle-dom

Dependency ranges changed: base, jsaddle, lens, text

API changes (from Hackage documentation)

+ JSDOM.Extras.WaitForJS: getGlobalJS :: MonadJSM m => Text -> m (Maybe JSVal)
+ JSDOM.Extras.WaitForJS: waitForGlobalJS :: MonadJSM m => Text -> m JSVal
+ JSDOM.Extras.WaitForJS: withGlobalJS :: MonadJSM m => Text -> (JSVal -> m a) -> m a

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # jsdom-extras changelog +## 0.1.1.0++* Add JSDOM.Extras.WaitForJS+ ## 0.1.0.0  * Initial release
README.md view
@@ -1,17 +1,127 @@ # jsdom-extras -Javascript interop convenience functions+Convenience utilities for working with **JSDOM / JSaddle** from Haskell. This package wraps a few common patterns you’ll reach for in browser-ish code: console logging, JSON ↔︎ JSVal conversion, small object helpers, and a tiny utility to wait for globals to appear. -[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/jsdom-extras.svg)](https://hackage.haskell.org/package/jsdom-extras) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/obsidiansystems/beam-automigrate/blob/master/LICENSE)+> **Motivation**+> If you’re doing Reflex-DOM or JSaddle work, you often need to bridge `aeson` values, poke at JS objects, or+> ensure a script on the page has loaded before using it. `jsdom-extras` provides a small, typed toolkit for that. +--- -* [JSON](src/JSDOM/Extras/JSON.hs)-  * JSON.stringify-  * JSON.parse-* [console](src/JSDOM/Extras/Console.hs)-  * console.log-* [Object](src/JSDOM/Extras/Object.hs): Convenience functions for convertion to/from JS Objects-  * toObject-  * fromObject-  * lookup-    +## Features++* **Console logging** via `console.log` without requiring `Show` or manual conversions.+* **JSON helpers:** `Value ↔︎ JSVal` that work on both GHCJS and JSaddle-native backends.+* **Object helpers:** Build, decompose, and query JavaScript `Object`s in a few lines.+* **Wait for globals:** Poll for a `window` global and run code once it’s available.++---++## Quick start++Below are small, self‑contained snippets. All examples assume:++```haskell+{-# LANGUAGE OverloadedStrings #-}+import Language.Javascript.JSaddle (JSM, JSVal, MonadJSM)+import qualified JSDOM.Extras.ConsoleLog as C+import qualified JSDOM.Extras.JSON       as J+import qualified JSDOM.Extras.Object     as O+import qualified JSDOM.Extras.WaitForJS  as W+```++### 1) Console logging++```haskell+hello :: MonadJSM m => m ()+hello = C.consoleLog ("Hello from Haskell!" :: JSM.String)+```++No `Show` burden: anything with `ToJSVal` is fair game.++### 2) JSON ↔︎ JSVal++```haskell+import Data.Aeson (Value(..), object, (.=))++mkUser :: JSM JSVal+mkUser = J.jsValFromJSON (object ["name" .= ("Ada" :: String), "id" .= (1 :: Int)])++roundTrip :: JSVal -> JSM (Maybe Value)+roundTrip v = J.jsValToJSON v++stringified :: JSVal -> JSM JSM.String+stringified v = J.stringify v++parsed :: JSM JSVal+parsed = J.parse =<< J.toJSVal ("{\"ok\":true}" :: JSM.String)+```++These functions are **platform‑independent**:++* On **GHCJS**, conversions use `toJSVal`/`fromJSVal`.+* On **JSaddle native**, we reuse JSaddle’s internal JSON bridges.++### 3) Object helpers++```haskell+mkObj :: MonadJSM m => m O.Object+mkObj = O.toObject [("answer", 42 :: Int), ("name", "Ada" :: JSM.String)]++readObj :: MonadJSM m => O.Object -> m [(JSM.String, JSVal)]+readObj = O.fromObject++findKey :: MonadJSM m => O.Object -> m (Maybe JSVal)+findKey o = O.lookup "answer" o++noop :: O.JSCallAsFunction+noop = O.doNothing+```++`lookup` gracefully treats `null` and `undefined` as `Nothing`, and `toMaybe` is exposed if you need it directly.++### 4) Wait for a global and use it++Useful when a script tag populates `window.SomeLib` asynchronously.++```haskell+useWhenReady :: MonadJSM m => m ()+useWhenReady = W.withGlobalJS "SomeLib" $ \lib -> do+  -- do something with `lib :: JSVal`+  C.consoleLog ("SomeLib is ready" :: JSM.String)+```++`waitForGlobalJS` polls at ~250ms intervals until the value is present (neither `null` nor `undefined`). If you want a non‑blocking probe, use `getGlobalJS` which returns `Maybe JSVal`.++---++## API reference++The library exposes four modules:++* `JSDOM.Extras.ConsoleLog`++  * `consoleLog :: (MonadJSM m, ToJSVal a) => a -> m ()`++* `JSDOM.Extras.JSON`++  * `jsValFromJSON :: Value -> JSM JSVal`+  * `jsValToJSON   :: JSVal -> JSM (Maybe Value)`+  * `stringify     :: MonadJSM m => JSVal -> m JSM.String`+  * `parse         :: MonadJSM m => JSVal -> m JSVal`++* `JSDOM.Extras.Object`++  * `type ToJSObject k v = (ToJSString k, ToJSVal v)`+  * `singleton   :: (ToJSObject a b, MonadJSM m) => a -> b -> m Object`+  * `toObject    :: (MonadJSM m, ToJSObject a b) => [(a, b)] -> m Object`+  * `fromObject  :: MonadJSM m => Object -> m [(JSM.String, JSVal)]`+  * `lookup      :: MonadJSM m => JSM.String -> Object -> m (Maybe JSVal)`+  * `toMaybe     :: MonadJSM m => JSVal -> m (Maybe JSVal)`+  * `doNothing   :: JSCallAsFunction`++* `JSDOM.Extras.WaitForJS`++  * `getGlobalJS   :: MonadJSM m => Text -> m (Maybe JSVal)`+  * `waitForGlobalJS :: MonadJSM m => Text -> m JSVal`+  * `withGlobalJS  :: MonadJSM m => Text -> (JSVal -> m a) -> m a`
jsdom-extras.cabal view
@@ -1,9 +1,9 @@-cabal-version:      >=1.10+cabal-version:      3.0 name:               jsdom-extras-version:            0.1.0.0+version:            0.1.1.0 synopsis:           Convenience utilities for JSDOM-description:        Commonly used javascript functions-license:            BSD3+description:        Handy javascript functions and bindings for web development+license:            BSD-3-Clause license-file:       LICENSE author:             Obsidian Systems LLC maintainer:         maintainer@obsidian.systems@@ -19,14 +19,14 @@     JSDOM.Extras.ConsoleLog     JSDOM.Extras.JSON     JSDOM.Extras.Object+    JSDOM.Extras.WaitForJS    build-depends:-      aeson        >=2.0   && <2.3-    , base         >=4.14  && <4.19-    , jsaddle      >=0.9.4 && <0.10-    , jsaddle-dom  >=0.9.4 && <0.10-    , lens         >=5.0.0 && <5.4-    , text         >=1.2.4 && <2.1+    , aeson    >=2.0   && <2.3+    , base     >=4.11  && <4.22+    , jsaddle  >=0.9.8 && <0.10+    , lens     >=5.0.1 && <5.4+    , text     >=1.2.4 && <2.2    hs-source-dirs:   src   default-language: Haskell2010
+ src/JSDOM/Extras/WaitForJS.hs view
@@ -0,0 +1,27 @@+{-# Language LambdaCase #-}+{-# Language OverloadedStrings #-}+{-# Language ScopedTypeVariables #-}+{-|+Description:+  A facility for making sure library-defined javascript variables are ready before we use them+-}+module JSDOM.Extras.WaitForJS where++import Control.Concurrent+import Control.Lens+import Control.Monad.IO.Class+import Data.Text (Text)+import Language.Javascript.JSaddle++getGlobalJS :: MonadJSM m => Text -> m (Maybe JSVal)+getGlobalJS varName = liftJSM $ do+  window <- jsg ("window" :: Text)+  maybeNullOrUndefined =<< (window ^. js varName)++waitForGlobalJS :: MonadJSM m => Text -> m JSVal+waitForGlobalJS varName = getGlobalJS varName >>= \case+  Nothing -> liftIO (threadDelay 250000) >> waitForGlobalJS varName+  Just var -> pure var++withGlobalJS :: MonadJSM m => Text -> (JSVal -> m a) -> m a+withGlobalJS varName f = waitForGlobalJS varName >>= f