Code Monkey home page Code Monkey logo

elm-hot's Introduction

elm-hot

elm-hot

This package provides the core infrastructure needed for doing hot code swapping in Elm. It supports Elm 0.19 only.

This low-level package is intended for authors of Elm application servers.

If you're looking for something that's easier to use, and you're willing to use Webpack, see elm-hot-webpack-loader, which is built using this package. Another option is Parcel which has built-in support for Elm and this package.

The goal of this package is to provide a reusable core that can be used to provide hot code swapping support in a variety of environments--not just Webpack.

Changelog

1.1.6

  • more lenient search for Browser.Navigation.Key in the generated JS
  • updated dependencies

1.1.5

  • update dependencies

1.1.4

  • fixed a bug where HMR failed because Browser.Navigation.Key changed location
  • fixed a crash when the app's Model contains Json.Encode.null

1.1.3

  • fixed a crash when using Elm debugger and elm/browser 1.0.2

1.1.2

  • fixed a bug where HMR would not work for very small ("toy") Elm projects

1.1.1

  • added support for Elm 0.19.1

1.0.1

  • bug fixes

1.0.0

  • improved Browser.application support (Browser.Navigation.Key can be stored anywhere in your model now)

0.9.1

  • separated the Webpack loader out into its own package
  • exposed core API

0.9.0

  • first release

Installing elm-hot core API

$ npm install --save elm-hot

Core API

function inject(str)

Injects the hot code swapping functionality into a compiled Elm app.

  • takes the Javascript code emitted by the Elm compiler as an input string
  • returns a string containing the injected code ready to be eval-ed in the browser.

Example of how the core API could be used

const elmHot = require('elm-hot');
const {compileToStringSync} = require('node-elm-compiler');
const injectedCode = elmHot.inject(compileToStringSync(["src/Main.elm"], {}));

In order to provide something similar to webpack-dev-server with hot module reloading, an application server could be developed to do the following:

  • serve a modified version of the app developer's index.html to receive push events from the server
  • watch .elm files on disk for changes
  • whenever a source file changes, push an event to the client notifying it that it should fetch new code from the server
  • when the client receives the event:
    • fetch the new code (the server will re-compile the Elm code and use elm-hot to inject the hot-code-swapping logic)
    • the client deletes the old Elm object and calls eval() on the new code from the server

I have implemented something similar to this for the integration tests. See test/server.js and test/client.js for inspiration.

The above description is probably a bit too vague, so if you would like more details, create an issue.


Attribution

Elm hot code swapping is based on the work of Flux Xu's elm-hot-loader.

elm-hot's People

Contributors

boudra avatar dependabot[bot] avatar dmerand avatar ianmackenzie avatar klazuka avatar lucamug avatar mjenssen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

elm-hot's Issues

Could not find Browser.Navigation.Key in your model with Browser.application

elm-hot log the error message:

[elm-hot] Hot-swapping disabled for Main: could not find Browser.Navigation.Key in your model.

with Browser.application and after a reloading:

...
[WDS] 100% - Compilation completed.
[WDS] App hot update...
[HMR] Checking for updates on the server...
Uncaught RangeError: Maximum call stack size exceeded
    at hotAddUpdateChunk (main.js:979)
    at webpackHotUpdateCallback (main.js:8)
    at webpackHotUpdateCallback (main.js:9)
   ...
Compiled in DEBUG mode. Follow the advice at https://elm-lang.org/0.19.0/optimize for better performance and smaller assets.
[elm-hot] Hot-swapping module: Main
[elm-hot] Hot-swapping disabled for Main: could not find Browser.Navigation.Key in your model.
...

Preserve scroll position on body

While using the Elm plugin for Parcel I've noticed that whenever the page I'm working on hot reloads, any scroll offset on the body gets reset. This is a problem as the time I spend scrolling back to where I was on a long page can add up.

I'm posting this issue here because, as I understand it, the Elm plugin for Parcel relies on elm-hot for hot reloading.

Release

Hi there, I've been starting to use elm-hot to work on the 0.19 upgrade of my project by referencing your repo in my package.json.

Are there any major issue left prior to the release on npm that I should be aware of ? I am well aware that elm-hot is in its early stages still.

Anyway, thank you for the amazing work. The (very-limited) tests I did so far did work well enough !

Elm Hot do not work with Browser.Navigation.Key

Hi!

I'm using elm-hot with Webpack, using v1.0.2. Unfortunately, I added Browser.Navigation in my Main file, but now I can't start the webpack-dev-server anymore because of this:

Error: [elm-hot] Browser.Navigation.Key def not found. Version mismatch?

Do you have an idea of what to do? I'm correctly storing the Browser.Navigation.Key in the Model (but nested).

Error "Cannot read property 'hasOwnProperty' of undefined"

I am getting this error if I activate the hot reload using elm-live. It is unclear to me if the error is related to elm-live implementation or to elm-hot.

I commented to a related issue in elm-live here: wking-io/elm-live#211

This is the error:

elm.js:37126 Uncaught TypeError: Cannot read property 'hasOwnProperty' of undefined
    at findNavKey (elm.js:37126)
    at hookedInit (elm.js:37218)
    at _Platform_initialize (elm.js:1978)
    at _Platform_initialize (elm.js:37256)
    at elm.js:4093
    at elm.js:20
    at Object.module.init (elm.js:36980)
    at index-hot.html:29

Screen Shot 2020-01-31 at 9 34 47

Screen Shot 2020-01-31 at 9 34 30

Hot reload failing

Hello!

I'm using create-elm-app on a project, which is using elm-hot under the hood. Here are the versions involved:

➜  npm ls elm-hot
[email protected] /Users/projects/my-project
└─┬ [email protected]
  └─┬ [email protected]
    └── [email protected] 

When changing some code in my IDE, the hot reload is not working and I can see this error in the console:

[elm-hot] Hot-swapping Main not possible: keypath state,a,b is invalid. Please report a bug.

with the stacktrace being:

__stack_frame_overlay_proxy_console__ | @ | index.js:2178
-- | -- | --
  | hookedInit | @ | Main.elm:21480
  | _Platform_initialize | @ | Main.elm:1877
  | _Platform_initialize | @ | Main.elm:21534
  | (anonymous) | @ | Main.elm:3991
  | (anonymous) | @ | Main.elm:20
  | swap | @ | Main.elm:21293
  | webpackHotUpdate../src/Main.elm.scope._elm_hot_loader_init | @ | Main.elm:21560
  | (anonymous) | @ | Main.elm:21578
  | ./src/Main.elm | @ | Main.elm:21583
  | __webpack_require__ | @ | bootstrap:785
  | hotApply | @ | bootstrap:709
  | (anonymous) | @ | bootstrap:363
  | Promise.then (async) |   |  
  | hotUpdateDownloaded | @ | bootstrap:362
  | hotAddUpdateChunk | @ | bootstrap:338
  | webpackHotUpdateCallback | @ | bootstrap:57
  | (anonymous) | @ | main.f81156501911093…c61.hot-update.js:1

Do you have any idea where this could come from? Would a SSCCE help?

Ignore the provided DOM node in init for "fullscreen" Elm apps

Browser.document and Browser.element take over the <body> element. But if the user provides a node when calling init() on their Elm module entry point from JS, we will register that DOM node as belonging to the instance. But we actually should ignore it (which is apparently what the Elm runtime does).

Failing to find old key because navKeyPath changes

We have several pages in our application, the key is passed between the pages when you transition between them. Some pages store the key in e.g

{
    page = FooPage { navigation: { key: <KEY>, ... } }
}

while another page stores it in:

{
    page = BarPage { navKey: <KEY>, ... }
}

Elm-Hot fails to find the key when it wants to hot-reload. Our assumtion is that this is because the path cached in hmr.js is no longer correct (instance.navKeyPath) and when it tries to look it up it fails giving the error:

keypath " + instance.navKeyPath + " is invalid. Please report a bug.

Could you look up the key again in the oldModel when it needs to reload?

Hot reload failing

Hello!

I'm using create-elm-app on a project, which is using elm-hot under the hood. Here are the versions involved:

➜  npm ls elm-hot
[email protected] /Users/projects/my-project
└─┬ [email protected]
  └─┬ [email protected]
    └── [email protected] 

When changing some code in my IDE, the hot reload is not working and I can see this error in the console:

[elm-hot] Hot-swapping Main not possible: keypath state,a,b is invalid. Please report a bug.

with the stacktrace being:

__stack_frame_overlay_proxy_console__ | @ | index.js:2178
-- | -- | --
  | hookedInit | @ | Main.elm:21480
  | _Platform_initialize | @ | Main.elm:1877
  | _Platform_initialize | @ | Main.elm:21534
  | (anonymous) | @ | Main.elm:3991
  | (anonymous) | @ | Main.elm:20
  | swap | @ | Main.elm:21293
  | webpackHotUpdate../src/Main.elm.scope._elm_hot_loader_init | @ | Main.elm:21560
  | (anonymous) | @ | Main.elm:21578
  | ./src/Main.elm | @ | Main.elm:21583
  | __webpack_require__ | @ | bootstrap:785
  | hotApply | @ | bootstrap:709
  | (anonymous) | @ | bootstrap:363
  | Promise.then (async) |   |  
  | hotUpdateDownloaded | @ | bootstrap:362
  | hotAddUpdateChunk | @ | bootstrap:338
  | webpackHotUpdateCallback | @ | bootstrap:57
  | (anonymous) | @ | main.f81156501911093…c61.hot-update.js:1

Do you have any idea where this could come from? Would a SSCCE help?

Handle Browser.Navigation.Key that is not at the root of the app model

Based on the discussion we add on Slack yesterday about the feasibility of such an improvement, I'm taking the liberty of opening this issue to easily track progress as well as coordinate efforts should some benevolent soul try and take a shot at this.

Some relevant information from @klazuka :

It can definitely be fixed (PRs welcome). At the time, I just wanted to get something out the door, and I hadn’t yet seen changes to rtfeldman’s SPA example. So it seemed like a reasonable compromise. But now that that example is out there, it’s less tenable of a solution.
The core problem is that Browser.Navigation.Key is not plain-old data: it’s actually a function that closes over the Elm runtime’s sendToApp function. So unlike all of the other data in your model, the navigation key cannot be reused in the new app. So we need to be able to find it in the old model and swap it out with the new app’s key.
My implementation just looks one level deep in your model. We could certainly crawl the model recursively to try to find all instances of the nav key and update them in the new app’s model.

Unless I'm mistaken, the relevant code should be around here : https://github.com/klazuka/elm-hot/blob/master/resources/hmr.js#L303-L325

Application hangs without Browser.Navigation.Key stored in Model

Environment

node -v: v8.11.4
npm -v: 6.3.0
npm ls create-elm-app -g: [email protected]

Operating system: Ubuntu
Browser and version (if relevant): Version 69.0.3497.92 (Official Build) (64-bit)

Steps to Reproduce

  1. Start new project with create-elm-app
  2. run elm install elm/url
  3. Paste Main.elm from Reproducible Demo section below
  4. Start application with elm-app start and open it in browser.

Expected Behavior

Provide warning message about missing setup that create-elm-app requires and don't break application. According to elm documentation:

A navigation Key is needed to create navigation commands that change the URL. That includes pushUrl, replaceUrl, back, and forward.

It's confusing that application compiles successfully but then breaks in runtime as soon as you apply Browser.application to run it.

Actual Behavior

Application hangs in infinite loop, from quick debugging I find out it's in findNavKey function.

Reproducible Demo

Modified Main.elm from create-elm-app

module Main exposing (..)

import Browser
import Browser.Navigation
import Html exposing (Html, text, div, h1, img)
import Html.Attributes exposing (src)
import Url


---- MODEL ----


type alias Model =
    {}


init : () -> Url.Url -> Browser.Navigation.Key -> ( Model, Cmd Msg )
init flags url navKey =
    ( {}, Cmd.none )



---- UPDATE ----


type Msg
    = NoOp
    | LinkClicked Browser.UrlRequest
    | UrlChnged Url.Url


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    ( model, Cmd.none )



---- VIEW ----


view : Model -> Browser.Document Msg
view model =
    {
        title = ""
        , body = [
            div []
                [ img [ src "/logo.svg" ] []
                , h1 [] [ text "Your Elm App is working!" ]
                ]
        ]
    }




---- PROGRAM ----


main : Program () Model Msg
main =
    Browser.application
        { view = view
        , init =  init
        , update = update
        , subscriptions = always Sub.none
        , onUrlChange = UrlChnged
        , onUrlRequest = LinkClicked
        }

Browser.element broken for certain DOM configurations

littlestudent on Slack wrote that HMR is broken for him with Browser.element

HMR fails to show the new changes with this DOM:

<body>
  <div id="root" class="fullHeight"></div>
</body>

but works with:

<body>
  <div>
    <div id="root" class="fullHeight"></div>
  </div>
</body>

Init without args gives 'TypeError': Cannot read property 'node' of undefined

The problem

I have an application where I call init with no arguments. If I leave it like that, then the hmr script throws.

If I add {} as the argument, it works.

// This throws
const app = Elm.Main.init();

// This works
const app = Elm.Main.init({});

The source

I traced that back to

var domNode = args['node'] ? wrapDomNode(args['node']) : document.body;

 if (originalInit) {
                module.init = function (args) {
                    var elm;
                    var portSubscribes = {};
                    var portSends = {};
                    var domNode = args['node'] ? wrapDomNode(args['node']) : document.body;

The code there assumes that args already exists, and then checks whether there's a node or not.

A solution

I thought to open an issue instead of sending a drive-by PR.
One solution could be just more ternaries, or splitting it out to something named and nicer to debug.

var domNode = !args ? document.body : args['node'] ? wrapDomNode(args['node']) : document.body;

But the root question I suppose is whether "init without arguments means document.body".
I looked through the docs, and that seems to be the case, at least for the current Browser programs.
It seems that package.elm-lang.org also uses init without args, and possibly more user code.

view-source:https://package.elm-lang.org/packages/elm/browser/latest/Browser#application

Small conflict with elm-optimize-level-2

I've been experimenting with elm-optimize-level-2 in our Parcel build (a large part of what I do is testing performance impact of code changes, so it's useful to be able to combine hot reloading with an optimized-but-not-minified build so I can make a change and get a quick sense of whether things are faster or slower). Unfortunately there's a small conflict between elm-optimize-level-2 and elm-hot; elm-hot expects the exact string

var key = function() { key.a(onUrlChange(_Browser_getUrl())); };

in the output, but the output from elm-optimize-level-2 has

var key = function () { key.a(onUrlChange(_Browser_getUrl())); };

(with a space between function and ()). For now I have a little post-processing script that makes the necessary replacement, but would it make sense to use a whitespace-tolerant regex instead of an exact string match on this line?

Store messages instead of `model`?

Currently if I change init or my initial model, those changes will not get hot-loaded because hmr.js remembers the last model state.

I made a proof of concept back in 0.18 of doing hot loading by storing init and the list of messages instead of model: https://gist.github.com/rtfeldman/f259128af7fea653876c34cca033ae68

When you change your code, including init or the initial model, it would replay the messages on top of the new code. This would also continue to work for any of the existing use cases, because the current model state can be derived by replaying messages on top of the initial model.

Thoughts?

Cannot init a headless Program

When the library gets used on top of a SharedWorker implemented as a worker program, the wrapping init function fails because document does not exist in the context of the worker.

The failing line in my case is hmr.js#L174 but L170 is going to have the same problem.

Adding a simple guard if (typeof document !== 'undefined') seems to solve the problem at least for me.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.