mozilla / qbrt

CLI to a Gecko desktop app runtime
Apache License 2.0
390 stars 31 forks source link

CommonJS require() loader #150

Open Happy-Ferret opened 6 years ago

Happy-Ferret commented 6 years ago

I'm currently working on Loader.jsm, a module to implement require()1.

The early PoC basically already works properly, finding local modules ( require("./moduleName") ) as well as modules inside dist/qbrt/lib, which I added for testing purposes and figured this is where the CommonJS interface brought up in #79 might live.

I only spent 20 minutes working on it, so it's still rough around the edges. Furthermore, my local qbrt fork isn't based on Firefox Nightly but on a fork of an older Gecko-Dev based around Firefox 56 (so the implementation for upstream qbrt will have to have a few lines changed. Shouldn't be a lot of work though).

Two reasons I won't share code, just yet.

That being said, I wonder if you'd be up for a brainstorming session. Would you be willing to jointly work out/standardize the shape of the loader, @mykmelez?

For starters. What do you think of node_modules? Should it be included in qbrt's implementation of the CommonJS loader mechanism2 ? How should qbrt (the hypothetical CommonJS interface mentioned in aforementioned issue) be exposed? As a builtin (i e require("qbrt") )? Or perhaps similar to the way the legacy Addon-SDK used to be exposed ( require("qbrt/moduleName") ) ?

1: Eventually I'd like to find a way to expose it as a global in a more direct fashion, too. Without const { require } = Cu.import('resource://qbrt/modules/Loader.jsm', {}); inside an app's main.js.

2: Personally, I'll say I'm not a fan of it. It seems more trouble than it's worth. But, alas, I might be missing something.

mykmelez commented 6 years ago

CommonJS interface brought up in #79

Erm, the "main module" I described in that issue is actually intended to be the entry point for using qbrt as a library within another Node package, i.e. the value of the package.json's main field.

Right now qbrt only provides an executable, and it only makes sense to install it globally; but I could imagine its functionality being useful to another Node package, if its APIs were exposed via a main module.

That being said, I wonder if you'd be up for a brainstorming session. Would you be willing to jointly work out/standardize the shape of the loader, @mykmelez?

Yes, I'm happy to discuss this!

Note that ES6 modules are steadily being implemented in Firefox, and support for module scripts (<script type="module">) was enabled a couple months ago in https://bugzilla.mozilla.org/show_bug.cgi?id=1438139, so we should already be able to use that in web contexts, like any HTML/XUL windows that an application's main.js script opens, even if not in main.js itself.

Thus I wonder how important it is to provide a CommonJS module loader. Even for main.js, perhaps it'd be better to wait for bug 1308512 - [meta] Migrate from Cu.import to ES6 Modules or some other way to load ES6 modules in that script, like the proposed import() statement.

Also, note that there are some existing module loaders in Firefox already, like https://searchfox.org/mozilla-central/source/devtools/shared/Loader.jsm and https://searchfox.org/mozilla-central/source/toolkit/components/workerloader/require.js. And there are implementations like https://stuk.github.io/require1k/ that are intended for web contexts but might be adaptable to main.js.

What do you think of node_modules? Should it be included in qbrt's implementation of the CommonJS loader mechanism2 ?

Hmm, I'm leery of that name, as it suggests that Node modules are supported, and many of them won't be. Although it's true that NPM is used to distribute modules intended for web contexts, so if you were using NPM to install such packages for use in web contexts, as opposed to main.js, then it might make sense. But then it should be possible to use module scripts there.

How should qbrt (the hypothetical CommonJS interface mentioned in aforementioned issue) be exposed? As a builtin (i e require("qbrt") )? Or perhaps similar to the way the legacy Addon-SDK used to be exposed ( require("qbrt/moduleName") ) ?

Setting aside the CommonJS question, I'm unsure how best to expose qbrt-specific interfaces to applications in general. Currently, the only public interfaces that qbrt exposes are in Runtime.jsm, which also contains some private interfaces (like Runtime.start).

It's also unclear to what extent qbrt will evolve its own APIs, as opposed to exposing the APIs of the underlying runtime. Nevertheless, my general inclination would be to use the best mechanism currently supported by the underlying runtime, which for pure JS module implementations is probably ES6 modules for web contexts and JSMs for main.js. (Native module implementations is a whole different issue for which I don't have any answer at all at the moment.)

Happy-Ferret commented 6 years ago

Erm, the "main module" I described in that issue is actually intended to be the entry point for using qbrt as a library within another Node package, i.e. the value of the package.json's main field.

Right now qbrt only provides an executable, and it only makes sense to install it globally; but I could imagine its functionality being useful to another Node package, if its APIs were exposed via a main module.

Hm. Either I misunderstand you or you understand me, or perhaps we misunderstand each other here. Basically, my understanding was to have a low-level module (not unlike Electron's electron module) one may import and use from main.js, to provide basic functionality.

i e (example blatantly stolen from Electron)

const {BrowserWindow} = require('qbrt');

let win = new BrowserWindow({width: 800, height: 600});
win.URL = "https://google.com";

Note that ES6 modules are steadily being implemented in Firefox, and support for module scripts ( Githubissues.

  • Githubissues is a development platform for aggregating issues.