Skip to content

Display FFI errors to user #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Bugfixes:
Other improvements:

- Use `replaceState` for setting query params (#266 by @ptrfrncsmrph)
- Display missing FFI dependency error message to user (#268 by @ptrfrncsmrph)

## [v2021-11-30.1](https://github.com/purescript/trypurescript/releases/tag/v2021-11-11.1)

Expand Down
6 changes: 6 additions & 0 deletions client/public/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ pre code {
background: none;
border: 0;
margin: 0;
overflow-x: auto;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}

iframe {
Expand Down
23 changes: 14 additions & 9 deletions client/src/Try/Container.purs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Ace (Annotation)
import Control.Monad.Except (runExceptT)
import Data.Array (fold)
import Data.Array as Array
import Data.Either (Either(..), hush)
import Data.Either (Either(..))
import Data.Foldable (for_, oneOf)
import Data.FoldableWithIndex (foldMapWithIndex)
import Data.Maybe (Maybe(..), fromMaybe, isNothing)
Expand Down Expand Up @@ -189,17 +189,22 @@ component = H.mkComponent
if settings.showJs then
H.liftEffect teardownIFrame
else do
mbSources <- H.liftAff $ map hush $ runExceptT $ runLoader loader (JS js)
eitherSources <- H.liftAff $ runExceptT $ runLoader loader (JS js)
for_ warnings \warnings_ -> do
let anns = Array.mapMaybe (toAnnotation MarkerWarning) warnings_
_ <- H.query _editor unit $ H.tell $ Editor.SetAnnotations anns
pure unit
for_ mbSources \sources -> do
let eventData = Object.insert "<file>" (JS js) sources
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) }
case eitherSources of
Right sources -> do
let eventData = Object.insert "<file>" (JS js) sources
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) }
Left err -> do
H.liftEffect teardownIFrame
H.liftEffect $ error err
H.modify_ _ { compiled = Just (Left err) }

HandleEditor (Editor.TextChanged text) -> do
_ <- H.fork $ handleAction $ Cache text
Expand Down Expand Up @@ -383,7 +388,7 @@ component = H.mkComponent

renderCompiled = case _ of
Left err ->
renderPlaintext "Unable to parse the response from the server."
Copy link
Contributor Author

@pete-murphy pete-murphy Feb 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, just realized why we weren't hitting this case even if we pass a bad loaderUrl. This line

H.modify_ _ { compiled = Just (Right res) }
was always running regardless of whatever errors happened when running the loader.

renderPlaintext err
Right res -> case res of
CompileFailed { error } -> case error of
OtherError err ->
Expand Down
20 changes: 14 additions & 6 deletions client/src/Try/Loader.purs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ module Try.Loader
import Prelude

import Control.Bind (bindFlipped)
import Control.Monad.Except (ExceptT)
import Control.Monad.Except (ExceptT, throwError)
import Control.Parallel (parTraverse)
import Data.Array (foldMap)
import Data.Array as Array
import Data.Array.NonEmpty as NonEmpty
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Newtype (unwrap)
import Data.String (Pattern(..))
import Data.String (Pattern(..), joinWith)
import Data.String as String
import Data.String.Regex (Regex)
import Data.String.Regex as Regex
Expand Down Expand Up @@ -112,10 +113,20 @@ makeLoader rootPath = Loader (go Object.empty <<< parseDeps "<file>")
deps = { name: _, path: Nothing } <$> shim.deps
pure { name, path, deps, src }
Nothing ->
pure { name, path, deps: [], src: ffiDep name }
throwError (missingFFIDep name)
liftEffect $ putModule name mod
pure mod

missingFFIDep :: String -> String
missingFFIDep name =
joinWith "\n" $
[ "Compilation succeeded, but the following FFI dependency is missing:"
, " - " <> name
, ""
, "We don't provide FFI shims for all libraries; for example, Node libraries are not supported on Try PureScript."
, "If you would like to suggest a new FFI shim be supported, please open an issue."
]

go :: Object JS -> Array Dependency -> ExceptT String Aff (Object JS)
go ms [] = pure ms
go ms deps = do
Expand All @@ -130,6 +141,3 @@ makeLoader rootPath = Loader (go Object.empty <<< parseDeps "<file>")
# bindFlipped _.deps
# Array.nubBy (comparing _.name)
# go ms'

ffiDep :: String -> JS
ffiDep name = JS $ "throw new Error('FFI dependency not provided: " <> name <> "');"