alexisvincent/systemjs-hot-reloader

Page reloading on every second file change

svi3c opened this issue · 7 comments

svi3c commented

Hi,

I get this warning after every two sequential changes of files:
missing exported value on http://localhost:9987/src/main.ts, reloading whole page because module record is broken
This seems to happen because moduleToDelete.exports evaluates to undefined and exportedValue ist undefined as well.

In a try to track the issue down, I recognized that the deleteModule() method was called twice and the exports of the module are undefined on the second call.

Do you have any suggestions what's going wrong here?

This is my entry point (src/main.ts):

import HotReloader from "systemjs-hot-reloader/hot-reloader";

export let __reload = () => console.log("__reload");

new HotReloader(`${window.location.protocol}//${window.location.hostname}:${window.location.port}`);

export let foo = "bar";

console.log("Module loaded");
svi3c commented

I'm using JSPM 17 beta 13 and I don't bundle.

My jspm.config looks like this:

SystemJS.config({
  transpiler: "plugin-typescript",
  packages: {
    "app": {
      "main": "main",
      "defaultExtension": "ts",
      "meta": {
        "*.ts": {
          "loader": "plugin-typescript"
        }
      }
    }
  }
});

SystemJS.config({
  packageConfigPaths: [
    "github:*/*.json",
    "npm:@*/*.json",
    "npm:*.json"
  ],
  map: {
    "angular2": "npm:angular2@2.0.0-beta.17",
    "es6-shim": "github:es-shims/es6-shim@0.35.0",
    "fs": "github:jspm/nodelibs-fs@0.2.0-alpha",
    "net": "github:jspm/nodelibs-net@0.2.0-alpha",
    "os": "github:jspm/nodelibs-os@0.2.0-alpha",
    "plugin-typescript": "github:frankwallis/plugin-typescript@4.0.7",
    "process": "github:jspm/nodelibs-process@0.2.0-alpha",
    "reflect-metadata": "npm:reflect-metadata@0.1.2",
    "rxjs": "npm:rxjs@5.0.0-beta.7",
    "systemjs-hot-reloader": "github:capaj/systemjs-hot-reloader@0.5.7",
    "tty": "github:jspm/nodelibs-tty@0.2.0-alpha",
    "util": "github:jspm/nodelibs-util@0.2.0-alpha",
    "zone.js": "npm:zone.js@0.6.12"
  },
  packages: {
    "github:capaj/systemjs-hot-reloader@0.5.7": {
      "map": {
        "debug": "npm:debug@2.2.0",
        "socket.io-client": "github:socketio/socket.io-client@1.4.5",
        "weakee": "npm:weakee@1.0.0"
      }
    },
    "github:frankwallis/plugin-typescript@4.0.7": {
      "map": {
        "typescript": "npm:typescript@1.8.10"
      }
    },
    "github:jspm/nodelibs-os@0.2.0-alpha": {
      "map": {
        "os-browserify": "npm:os-browserify@0.2.1"
      }
    },
    "npm:debug@2.2.0": {
      "map": {
        "ms": "npm:ms@0.7.1"
      }
    },
    "npm:rxjs@5.0.0-beta.7": {
      "map": {
        "symbol-observable": "npm:symbol-observable@0.2.4"
      }
    }
  }
});
svi3c commented

Here are some screenshots. I added some logging to the hot-reloader.js

1

2

Maybe you could add more logs, to see what plugin-typescript transpiles your file to, to see if that contains strange code? Maybe compare it to how es6 code is transpiled?

svi3c commented

The output of plugin-typescript looks like this:

(function(System, SystemJS) {System.register(["systemjs-hot-reloader/hot-reloader"], function(exports_1, context_1) {
    "use strict";
    var __moduleName = context_1 && context_1.id;
    var hot_reloader_1;
    var __reload, foo;
    return {
        setters:[
            function (hot_reloader_1_1) {
                hot_reloader_1 = hot_reloader_1_1;
            }],
        execute: function() {
            exports_1("__reload", __reload = function () { return console.log("__reload"); });
            new hot_reloader_1.default(window.location.protocol + "//" + window.location.hostname + ":" + window.location.port);
            exports_1("foo", foo = "bar");
            console.log("Module loaded");
        }
    }
});

})(System, System);

Using plugin-babel to compile exactly the same file I get this:

(function(System, SystemJS) {"use strict";

System.register(["systemjs-hot-reloader/hot-reloader"], function (_export, _context) {
  var HotReloader, __reload, foo;

  return {
    setters: [function (_systemjsHotReloaderHotReloader) {
      HotReloader = _systemjsHotReloaderHotReloader.default;
    }],
    execute: function () {
      _export("__reload", __reload = function __reload() {
        return console.log("__reload");
      });

      _export("__reload", __reload);

      new HotReloader(window.location.protocol + "//" + window.location.hostname + ":" + window.location.port);

      _export("foo", foo = "bar");

      _export("foo", foo);

      console.log("Module loaded");
    }
  };
});
})(System, System);

Both outputs look equivalent to me, aside from the fact that the export of foo is called twice in the babel output.

The problem also occurrs when using plugin-babel.

I am using "systemjs-hot-reloader": "github:capaj/systemjs-hot-reloader@0.5.7". I also tested versions 0.5.0 and 0.4.0 ... Same behavior.

The fs-events add and unlink are generated when using IntelliJ Idea. But the problem also exists when using atom where I only get the change event (the hot reloader only listens on the change event).

svi3c commented

Both calls to hotReload() are done from line 76.
Is it expected behavior that there is a second web socket opening (see screenshots above)? The latter created socket retrieves the change event to the file earlier than the already open socket.
I'm using "chokidar-socket-emitter": "^0.5.1" as event source.

svi3c commented

Okay, found the problem:
the HotReloader was instantiated on ervery module reload. This caused a new socket opening and since the HotReloader.prototype.currentHotReload was not defined on the new instance of HotReloader.
Solution: I Built a setup.ts module which handles all the stuff which is only required once on page load and the main.ts imports this setup.

setup.ts:

import "zone.js";
import "reflect-metadata";
import HotReloader from "systemjs-hot-reloader/hot-reloader";

export let hotReloader = new HotReloader();

main.ts

import "./setup";
import {bootstrap} from "angular2/platform/browser";
import {TestComponent} from "./testComponent";

export let __reload = (m) => {
  // do stuff
};

bootstrap(TestComponent);

This does not happen if you use the systemjs-hot-reloader/default-listener module, since the module is a singleton by nature and only executes the HotReloader once.

Ah, that makes sense, great debugging work.
I usually initiate the hot reloader in the script tag in html that also loads my main javascript file, because I also need to enable trace there. That way I can also combine it into a snippet.