From 27a00ed860b8d1f6298ff4d03773b417a25e650c Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Fri, 10 Jun 2022 08:05:14 -0500 Subject: [PATCH 1/9] Update package set to latest; drop globals/math --- client/packages.dhall | 23 +++-------------------- client/spago.dhall | 2 -- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/client/packages.dhall b/client/packages.dhall index 75331a36..c1888031 100644 --- a/client/packages.dhall +++ b/client/packages.dhall @@ -1,22 +1,5 @@ let upstream = - https://github.com/purescript/package-sets/releases/download/psc-0.13.6-20200404/packages.dhall sha256:f239f2e215d0cbd5c203307701748581938f74c4c78f4aeffa32c11c131ef7b6 + https://github.com/purescript/package-sets/releases/download/psc-0.15.2-20220609/packages.dhall + sha256:2b15922dcc47143e5f271a75d4aa91b0379bc1bd7d880adfb428e287617210f7 -let additions = - { ace = - { repo = "https://github.com/purescript-contrib/purescript-ace.git" - , version = "v7.0.0" - , dependencies = - [ "arrays" - , "console" - , "effect" - , "foreign" - , "nullable" - , "prelude" - , "refs" - , "web-html" - , "web-uievents" - ] - } - } - -in upstream // additions +in upstream diff --git a/client/spago.dhall b/client/spago.dhall index d8c65090..31b0cf3f 100644 --- a/client/spago.dhall +++ b/client/spago.dhall @@ -18,12 +18,10 @@ , "foreign-object" , "functions" , "functors" - , "globals" , "halogen" , "identity" , "integers" , "js-timers" - , "math" , "maybe" , "node-fs" , "ordered-collections" From cff048f16470db69c4dd51895ec0d4a69282f561 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Fri, 10 Jun 2022 08:38:31 -0500 Subject: [PATCH 2/9] Update client code to PS 0.15.2 --- client/spago.dhall | 20 +++++---- client/src/Main.purs | 4 +- client/src/Try/API.purs | 12 ++--- client/src/Try/Container.purs | 77 ++++++++++++++++----------------- client/src/Try/Editor.purs | 14 +++--- client/src/Try/Gist.js | 6 +-- client/src/Try/Gist.purs | 2 +- client/src/Try/GitHub.purs | 2 +- client/src/Try/QueryString.js | 10 ++--- client/src/Try/QueryString.purs | 8 +++- client/src/Try/Session.js | 10 ++--- client/test/Main.purs | 4 +- 12 files changed, 86 insertions(+), 83 deletions(-) diff --git a/client/spago.dhall b/client/spago.dhall index 31b0cf3f..44a9bbdf 100644 --- a/client/spago.dhall +++ b/client/spago.dhall @@ -3,14 +3,15 @@ [ "ace" , "aff" , "affjax" + , "affjax-web" , "argonaut-codecs" + , "argonaut-core" , "arrays" , "assert" , "bifunctors" , "console" - , "const" , "control" - , "debug" + , "datetime" , "effect" , "either" , "exceptions" @@ -19,23 +20,24 @@ , "functions" , "functors" , "halogen" - , "identity" + , "halogen-subscriptions" , "integers" , "js-timers" + , "js-uri" + , "lists" , "maybe" + , "newtype" + , "node-buffer" , "node-fs" - , "ordered-collections" - , "parallel" + , "nullable" + , "partial" , "prelude" - , "profunctor" - , "psci-support" , "random" , "refs" - , "semirings" , "strings" , "transformers" , "tuples" - , "unfoldable" + , "unsafe-coerce" , "web-html" ] , packages = ./packages.dhall diff --git a/client/src/Main.purs b/client/src/Main.purs index 8c3d781c..2ba63761 100644 --- a/client/src/Main.purs +++ b/client/src/Main.purs @@ -9,6 +9,6 @@ import Halogen.VDom.Driver (runUI) import Try.Container as Container main :: Effect Unit -main = launchAff_ do +main = void $ launchAff_ do body <- HA.awaitBody - runUI Container.component unit body + void $ runUI Container.component unit body diff --git a/client/src/Try/API.purs b/client/src/Try/API.purs index afe533c6..a4660254 100644 --- a/client/src/Try/API.purs +++ b/client/src/Try/API.purs @@ -14,13 +14,15 @@ module Try.API import Prelude import Affjax (URL, printError) -import Affjax as AX import Affjax.RequestBody as AXRB import Affjax.ResponseFormat as AXRF import Affjax.StatusCode (StatusCode(..)) +import Affjax.Web as AX import Control.Alt ((<|>)) import Control.Monad.Except (ExceptT(..)) -import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:)) +import Data.Argonaut.Decode (class DecodeJson, JsonDecodeError(..), decodeJson, printJsonDecodeError, (.:)) +import Data.Argonaut.Encode (encodeJson) +import Data.Bifunctor (lmap) import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) import Data.Traversable (traverse) @@ -53,8 +55,8 @@ instance decodeJsonCompileError :: DecodeJson CompileError where map OtherError $ decodeJson contents "CompilerErrors" -> map CompilerErrors $ traverse decodeJson =<< decodeJson contents - _ -> - Left "Tag must be one of: OtherError, CompilerErrors" + j -> + Left $ AtKey "tag" $ UnexpectedValue $ encodeJson j type Suggestion = { replacement :: String @@ -105,6 +107,6 @@ compile endpoint code = ExceptT $ liftAff $ AX.post AXRF.json (endpoint <> "/com Right { status } | status >= StatusCode 400 -> pure $ Left $ "Received error status code: " <> show status Right { body } -> - pure $ Right $ decodeJson body + pure $ Right $ lmap printJsonDecodeError $ decodeJson body where requestBody = Just $ AXRB.string code diff --git a/client/src/Try/Container.purs b/client/src/Try/Container.purs index d09dc9a4..74868a4d 100644 --- a/client/src/Try/Container.purs +++ b/client/src/Try/Container.purs @@ -4,13 +4,11 @@ import Prelude import Ace (Annotation) import Control.Monad.Except (runExceptT) -import Data.Array (fold) import Data.Array as Array import Data.Either (Either(..), either) -import Data.Foldable (for_, oneOf) +import Data.Foldable (for_, oneOf, fold) import Data.FoldableWithIndex (foldMapWithIndex) import Data.Maybe (Maybe(..), fromMaybe, isNothing) -import Data.Symbol (SProxy(..)) import Data.String as String import Data.String (Pattern(..)) import Data.String.Regex as Regex @@ -34,6 +32,7 @@ import Try.Gist (getGistById, tryLoadFileFromGist) import Try.GitHub (getRawGitHubFile) import Try.QueryString (getQueryStringMaybe) import Try.Session (createSessionIdIfNecessary, storeSession, tryRetrieveSession) +import Type.Proxy (Proxy(..)) import Web.HTML (window) import Web.HTML.Window (alert) @@ -81,15 +80,15 @@ data Action | Compile (Maybe String) | HandleEditor Editor.Output -_editor :: SProxy "editor" -_editor = SProxy +_editor :: Proxy "editor" +_editor = Proxy type LoadCb = Effect Unit type FailCb = Effect Unit foreign import setupIFrame :: EffectFn3 { code :: String } LoadCb FailCb Unit foreign import teardownIFrame :: Effect Unit -component :: forall q i o. H.Component HH.HTML q i o Aff +component :: forall q i o. H.Component q i o Aff component = H.mkComponent { initialState , render @@ -129,7 +128,7 @@ component = H.mkComponent -- Set the editor contents. This will trigger a change event, causing a -- cache + compile step. - void $ H.query _editor unit $ H.tell $ Editor.SetEditorContent code + H.tell _editor unit $ Editor.SetEditorContent code UpdateSettings k -> do old <- H.get @@ -152,12 +151,12 @@ component = H.mkComponent H.modify_ _ { compiled = Nothing } code <- case mbCode of Nothing -> do - mbText <- H.query _editor unit $ H.request $ Editor.GetEditorContent + mbText <- H.request _editor unit $ Editor.GetEditorContent pure $ fold $ join mbText Just text -> pure text - _ <- H.query _editor unit $ H.tell $ Editor.SetAnnotations [] - _ <- H.query _editor unit $ H.tell $ Editor.RemoveMarkers + H.tell _editor unit $ Editor.SetAnnotations [] + H.tell _editor unit $ Editor.RemoveMarkers runExceptT (API.compile Config.compileUrl code) >>= case _ of Left err -> do H.liftEffect teardownIFrame @@ -176,21 +175,19 @@ component = H.mkComponent pure unit CompilerErrors errs -> do let anns = Array.mapMaybe (toAnnotation MarkerError) errs - _ <- H.query _editor unit $ H.tell $ Editor.SetAnnotations anns + H.tell _editor unit $ Editor.SetAnnotations anns for_ errs \{ position } -> for_ position \pos -> do - _ <- H.query _editor unit $ H.tell $ Editor.AddMarker MarkerError pos - pure unit + H.tell _editor unit $ Editor.AddMarker MarkerError pos - Right (Right res@(CompileSuccess { js, warnings })) -> do + Right res@(Right (CompileSuccess { js, warnings })) -> do { settings } <- H.get if settings.showJs then H.liftEffect teardownIFrame else do for_ warnings \warnings_ -> do let anns = Array.mapMaybe (toAnnotation MarkerWarning) warnings_ - _ <- H.query _editor unit $ H.tell $ Editor.SetAnnotations anns - pure unit + H.tell _editor unit $ Editor.SetAnnotations anns let importRegex :: Regex.Regex importRegex = either (\_ -> unsafeCrashWith "Invalid regex") identity @@ -207,7 +204,7 @@ component = H.mkComponent H.liftAff $ makeAff \f -> do runEffectFn3 setupIFrame eventData (f (Right unit)) (f (Left $ Aff.error "Could not load iframe")) mempty - H.modify_ _ { compiled = Just (Right res) } + H.modify_ _ { compiled = Just res } HandleEditor (Editor.TextChanged text) -> do _ <- H.fork $ handleAction $ Cache text @@ -217,9 +214,9 @@ component = H.mkComponent render :: State -> H.ComponentHTML Action Slots Aff render state = HH.div - [ HP.id_ "wrapper" ] + [ HP.id "wrapper" ] [ HH.div - [ HP.id_ "body" ] + [ HP.id "body" ] [ renderMenu , renderMobileBanner , renderEditor @@ -228,10 +225,10 @@ component = H.mkComponent where renderMenu = HH.ul - [ HP.id_ "menu" ] + [ HP.id "menu" ] [ HH.a [ HP.class_ $ HH.ClassName "menu-item" - , HP.id_ "home_link" + , HP.id "home_link" , HP.href "/" , HP.title "Try PureScript!" ] @@ -247,7 +244,7 @@ component = H.mkComponent [ HP.title "Select a view mode" ] [ HH.text "View Mode" ] , let name = "view_mode" in HH.ul - [ HP.id_ name ] + [ HP.id name ] [ menuRadio { checked: state.settings.viewMode == SideBySide , name @@ -298,25 +295,25 @@ component = H.mkComponent , HH.li [ HP.class_ $ HH.ClassName "menu-item no-mobile" ] [ HH.label - [ HP.id_ "compile_label" + [ HP.id "compile_label" , HP.title "Compile Now" - , HE.onClick \_ -> Just (Compile Nothing) + , HE.onClick \_ -> Compile Nothing ] [ HH.text "Compile" ] ] , HH.li [ HP.class_ $ HH.ClassName "menu-item nowrap no-mobile" ] [ HH.input - [ HP.id_ "auto_compile" + [ HP.id "auto_compile" , HP.name "auto_compile" , HP.title "Toggle auto-compilation of the file on code changes" , HP.value "auto_compile" , HP.type_ HP.InputCheckbox , HP.checked state.settings.autoCompile - , HE.onChecked \bool -> Just $ UpdateSettings (_ { autoCompile = bool }) + , HE.onChecked \bool -> UpdateSettings (_ { autoCompile = bool }) ] , HH.label - [ HP.id_ "auto_compile-label" + [ HP.id "auto_compile-label" , HP.for "auto_compile" , HP.title "Compile on code changes" ] @@ -325,16 +322,16 @@ component = H.mkComponent , HH.li [ HP.class_ $ HH.ClassName "menu-item nowrap" ] [ HH.input - [ HP.id_ "showjs" + [ HP.id "showjs" , HP.name "showjs" , HP.title "Show resulting JavaScript code instead of output" , HP.value "showjs" , HP.type_ HP.InputCheckbox , HP.checked state.settings.showJs - , HE.onChecked \bool -> Just $ UpdateSettings (_ { showJs = bool }) + , HE.onChecked \bool -> UpdateSettings (_ { showJs = bool }) ] , HH.label - [ HP.id_ "showjs_label" + [ HP.id "showjs_label" , HP.for "showjs" , HP.title "Show resulting JavaScript code instead of output" ] @@ -343,12 +340,12 @@ component = H.mkComponent , HH.li [ HP.class_ $ HH.ClassName "menu-item" ] [ HH.a - [ HP.id_ "helplink" + [ HP.id "helplink" , HP.href "https://github.com/purescript/trypurescript/blob/master/README.md" , HP.target "trypurs_readme" ] [ HH.label - [ HP.id_ "help" + [ HP.id "help" , HP.title "Learn more about Try PureScript" ] [ HH.text "Help" ] @@ -363,28 +360,28 @@ component = H.mkComponent renderEditor = HH.div - [ HP.id_ "editor_view" + [ HP.id "editor_view" , HP.attr (HH.AttrName "data-view-mode") case state.settings.viewMode of SideBySide -> "sidebyside" Code -> "code" Output -> "output" ] [ HH.div - [ HP.id_ "column1" + [ HP.id "column1" , HP.class_ $ HH.ClassName "no-mobile" ] - [ HH.slot _editor unit Editor.component unit (Just <<< HandleEditor) ] + [ HH.slot _editor unit Editor.component unit HandleEditor ] , HH.div [ HP.class_ $ HH.ClassName "separator" ] [ ] , HH.div - [ HP.id_ "column2_wrapper" ] + [ HP.id "column2_wrapper" ] [ HH.div - [ HP.id_ "column2" ] + [ HP.id "column2" ] [ maybeElem state.compiled renderCompiled ] , whenElem (isNothing state.compiled) \_ -> HH.div - [ HP.id_ "loading" ] + [ HP.id "loading" ] [ ] ] ] @@ -440,8 +437,8 @@ menuRadio props = [ HP.type_ HP.InputRadio , HP.name props.name , HP.value props.value - , HP.id_ props.id - , HE.onClick \_ -> Just props.onClick + , HP.id props.id + , HE.onClick \_ -> props.onClick , HP.checked props.checked ] , HH.label diff --git a/client/src/Try/Editor.purs b/client/src/Try/Editor.purs index 54c83d4d..8118d251 100644 --- a/client/src/Try/Editor.purs +++ b/client/src/Try/Editor.purs @@ -25,13 +25,14 @@ import Data.Newtype (class Newtype) import Data.Time.Duration (Milliseconds(..)) import Data.Traversable (traverse) import Effect (Effect) +import Effect.Class (liftEffect) import Effect.Aff.Class (class MonadAff) import Effect.Ref as Ref import Effect.Timer (clearTimeout, setTimeout) import Halogen as H import Halogen.HTML as HH import Halogen.HTML.Properties as HP -import Halogen.Query.EventSource as ES +import Halogen.Subscription as HS import Try.API (ErrorPosition) import Web.HTML (HTMLElement) @@ -72,7 +73,7 @@ data Action | ClearMarkers | HandleChange -component :: forall i m. MonadAff m => H.Component HH.HTML Query i Output m +component :: forall i m. MonadAff m => H.Component Query i Output m component = H.mkComponent { initialState , render @@ -96,7 +97,7 @@ component = H.mkComponent render _ = HH.div [ HP.ref $ H.RefLabel "ace" - , HP.id_ "code" + , HP.id "code" ] [ ] @@ -107,10 +108,11 @@ component = H.mkComponent editor <- H.liftEffect $ setupEditor element H.modify_ _ { editor = Just editor } session <- H.liftEffect $ Editor.getSession editor - void $ H.subscribe $ ES.effectEventSource \emitter -> do - emit <- debounce debounceTime \_ -> ES.emit emitter HandleChange + { emitter, listener } <- liftEffect $ HS.create + void $ H.subscribe emitter + H.liftEffect do + emit <- debounce debounceTime \_ -> HS.notify listener HandleChange EditSession.onChange session emit - pure mempty Finalize -> do handleAction ClearMarkers diff --git a/client/src/Try/Gist.js b/client/src/Try/Gist.js index 909b5413..9d527c23 100644 --- a/client/src/Try/Gist.js +++ b/client/src/Try/Gist.js @@ -1,9 +1,7 @@ -"use strict"; - -exports.rawUrl_ = function (gistInfo, filename) { +export function rawUrl_(gistInfo, filename) { if (gistInfo.files && gistInfo.files.hasOwnProperty(filename)) { return gistInfo.files[filename].raw_url; } else { return null; } -}; +} diff --git a/client/src/Try/Gist.purs b/client/src/Try/Gist.purs index 141260b1..ef5808c5 100644 --- a/client/src/Try/Gist.purs +++ b/client/src/Try/Gist.purs @@ -8,10 +8,10 @@ module Try.Gist import Prelude import Affjax (printError) -import Affjax as AX import Affjax.RequestBody as AXRB import Affjax.ResponseFormat as AXRF import Affjax.StatusCode (StatusCode(..)) +import Affjax.Web as AX import Control.Monad.Except.Trans (ExceptT(..)) import Data.Argonaut.Core (Json, caseJsonObject, stringify, toString) import Data.Argonaut.Encode (encodeJson) diff --git a/client/src/Try/GitHub.purs b/client/src/Try/GitHub.purs index 6a2aa07d..5c2f4b7b 100644 --- a/client/src/Try/GitHub.purs +++ b/client/src/Try/GitHub.purs @@ -3,9 +3,9 @@ module Try.GitHub where import Prelude import Affjax (URL, printError) -import Affjax as AX import Affjax.ResponseFormat as AXRF import Affjax.StatusCode (StatusCode(..)) +import Affjax.Web as AX import Control.Monad.Except (ExceptT(..)) import Data.Either (Either(..)) import Effect.Aff (Aff) diff --git a/client/src/Try/QueryString.js b/client/src/Try/QueryString.js index bbddc5f3..5f3f03c5 100644 --- a/client/src/Try/QueryString.js +++ b/client/src/Try/QueryString.js @@ -1,13 +1,11 @@ -"use strict"; - -exports.getQueryString = function() { +export function getQueryString() { return window.location.search; -}; +} -exports.setQueryParameters = function(params) { +export function setQueryParameters(params) { var encodedParams = Object.keys(params).map(function(key) { return key + '=' + encodeURIComponent(params[key].replace('/', '')); }).join('&'); window.history.replaceState(null, '', '?' + encodedParams); -}; +} diff --git a/client/src/Try/QueryString.purs b/client/src/Try/QueryString.purs index 8009936a..cb7bf4cb 100644 --- a/client/src/Try/QueryString.purs +++ b/client/src/Try/QueryString.purs @@ -15,7 +15,13 @@ import Data.Tuple (Tuple(..)) import Effect (Effect) import Effect.Uncurried (EffectFn1, runEffectFn1) import Foreign.Object as Object -import Global.Unsafe (unsafeDecodeURIComponent) +import JSURI (decodeURIComponent) +import Partial.Unsafe (unsafeCrashWith) + +unsafeDecodeURIComponent :: String -> String +unsafeDecodeURIComponent s = case decodeURIComponent s of + Just str -> str + Nothing -> unsafeCrashWith $ "unsafeDecodeURIComponent encountered errors when decoding '" <> s <> "'" foreign import getQueryString :: Effect String diff --git a/client/src/Try/Session.js b/client/src/Try/Session.js index 42c9971a..7edfd148 100644 --- a/client/src/Try/Session.js +++ b/client/src/Try/Session.js @@ -1,13 +1,11 @@ -"use strict"; - -exports.storeSession_ = function(sessionId, state) { +export function storeSession_(sessionId, state) { if (window.localStorage) { localStorage.setItem(sessionId, state.code); localStorage.setItem(sessionId + 'backend', state.backend); } -}; +} -exports.tryRetrieveSession_ = function(sessionId) { +export function tryRetrieveSession_(sessionId) { if (window.localStorage) { var code = localStorage.getItem(sessionId); var backend = localStorage.getItem(sessionId + 'backend'); @@ -15,4 +13,4 @@ exports.tryRetrieveSession_ = function(sessionId) { return { code: code, backend: backend }; } } -}; +} diff --git a/client/test/Main.purs b/client/test/Main.purs index 2bc4ed27..4999e106 100644 --- a/client/test/Main.purs +++ b/client/test/Main.purs @@ -3,7 +3,7 @@ module Test.Main where import Prelude import Data.Argonaut.Core (Json) -import Data.Argonaut.Decode (class DecodeJson, decodeJson) +import Data.Argonaut.Decode (class DecodeJson, JsonDecodeError, decodeJson) import Data.Bitraversable (ltraverse) import Data.Either (Either, isRight) import Effect (Effect) @@ -27,7 +27,7 @@ apiTests fixtures = do -- | Test that a JSON response decodes successfully. shouldDecode :: forall a. DecodeJson a => Proxy a -> Json -> Effect Unit shouldDecode _ fixture = do - result <- (decodeJson fixture :: Either String a) # ltraverse \errors -> do + result <- (decodeJson fixture :: Either JsonDecodeError a) # ltraverse \errors -> do log "Failed to decode fixture:\n" logShow errors assert (isRight result) From f072566ea27767d16db83ba2c6df186b67f56df8 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Fri, 10 Jun 2022 08:38:46 -0500 Subject: [PATCH 3/9] Move jquery code to Container.js FFI --- client/public/index.html | 61 ------------------------------------- client/src/Try/Container.js | 61 +++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 64 deletions(-) diff --git a/client/public/index.html b/client/public/index.html index 44a29b24..2cdc56ce 100644 --- a/client/public/index.html +++ b/client/public/index.html @@ -19,67 +19,6 @@ - diff --git a/client/src/Try/Container.js b/client/src/Try/Container.js index 8c26089e..27af2951 100644 --- a/client/src/Try/Container.js +++ b/client/src/Try/Container.js @@ -1,4 +1,59 @@ -"use strict"; +$.ajaxSetup({ + dataType: "text", +}); -exports.setupIFrame = setupIFrame; -exports.teardownIFrame = teardownIFrame; +export function teardownIFrame() { + var $ctr = $("iframe#output-iframe"); + $ctr.remove() +} + +export function setupIFrame(data, loadCb, failCb) { + var $ctr = $("#column2"); + var $iframe = $( + '