n-riesco / ijavascript

IJavascript is a javascript kernel for the Jupyter notebook
Other
2.19k stars 185 forks source link

autoreload ? #208

Closed parmentelat closed 2 years ago

parmentelat commented 4 years ago

Hi

I was just wondering if there currently exists a means to achieve what the %autoreload magic offers with a IPython kernel

namely I currently experience a very slow workflow, because each time I change a module that I load with require(), I need to restart the ijavascript kernel so that my change is taken into account

thanks in advance for any tip that would solve this impediment

and thanks for this great tool as well, regardless :)

n-riesco commented 4 years ago

Preloading modules was the initial use case I thought for the flag --startup-script=path/to/your/startup-script.js; e.g.:

$ ijsinstall --startup-script=/home/user/.ijs.rc.js
[/home/user/.ijs.rc.js]
var WebSocket = require('ws');

Please, let me know if this isn't enough.

parmentelat commented 4 years ago

hi; thanks for the prompt answer

I found something that kind of does what I needed; I meant something along those lines:

function autoreload(module) {
    require("fs").watchFile(
        require("path").resolve(module),
        () => {
            console.log(`unloading ${module}`)
            delete require.cache[require.resolve(module)]})}

point being, I trigger a notebook, that loads a module, but it's not working quite well yet, so I need to tweak it - possibly a lot

without that tool this essentially requires a kernel start, which takes ages in this context

that's why I was referring to IPython's %autoreload magic, that addresses this need quite nicely; using the above js autoreload function works but that requires me to spot and to name each module individually, so it's better but still a bit of a hassle

I thought I'd share this in case others meet the same kind of needs

n-riesco commented 4 years ago

Here's an untested idea:

(function() {
    const _require = global.require;

    global.require = function require(id) {
        const isAModulePath = (id || "").startsWith(".");
        if (isAModulePath) {
            return _require(id);
        }

        const modulePath = require.resolve(id);

        // Ensure only one watcher per module path
        const isCached = Boolean(require.cache[modulePath]);
        if (!isCached) {
            require("fs").WatchFile(modulePath, function autoreload() {
                delete require.cache[modulePath];
                console.log("autoreload: unloaded module " + id);

                // TODO: use smarter approach to `global[id]` (e.g. filter scope names, ...)
                global[id] = _require(modulePath);
            });
        }

        return _require(id);
    };
})();

Note to myself: doc for %autoreload extension

xianbaoqian commented 1 year ago

the flag --startup-script=path/to/your/startup-script.js

Hi @n-riesco Thanks for the code snippets. However I copied the content in the notebook but it doesn't seem to reload the file. I changed the file content, then require(FILE) again, but the variable is not updated. (Sry if I miss something. I'm pretty new to node).

It would be really great if we can make autoreload working in the same way of in Python. :-) Thx!

image
n-riesco commented 1 year ago

@xianbaoqian For context and the reasons why IJavsScript doesn't support magic commands see #43.

If you're still interested in having magic commands, please, see magichpatch.

xianbaoqian commented 1 year ago

Ah didn't notice that. Thanks! @n-riesco

xianbaoqian commented 1 year ago

Unfortunately load_ext is also not supported by magichpatch neither but it will be another issue.