-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
Splitting node in to more projects #9
Comments
/cc @defunctzombie who has already been experimenting with libuv.js |
@mikeal I like the proposal of having node core modules separated out with an interface you could call from any |
Some notes for those interested In experimenting with libuv.js a few things became clear to me:
tl;dr libuv.js is for distribution makers. Anything that isn't tied to libuv is an opinion and should be a module. A distribution is a set of predefined modules. It should be easy to make a distribution. Modules are amazing because they are versioned and easier to upgrade piece-by-piece. Underlying software stacks are not. |
Just a 5 cents from me: current TLS implementation in v0.12 does depend on being able to use libuv from C++. libuv.js could be used, but it'll need to provide an External handle and a C++ API for tls_wrap.cc to work. |
@indutny perhaps we should add a "crypto.js" or "tls.js" to the list of top level projects? |
@mikeal I don't mind, but it seems to be unrelated to my previous comment ;) I was just trying to say that these modules should expose C++ APIs as well as the JS stuff, and this could be done with |
been using bmeck/node-module-system to do customized |
👍 IMHO all the core modules should just be in npm instead of built in. Node itself could just be a require system for loading modules from npm and binding with libuv and v8. Not sure how that would work, but it would be awesome to be able to update the core modules independently from node itself and from the other core modules (i.e. more frequently). |
This is interesting. We just started binding JSC to libuv for kicks doing our first pass at a ServiceWorker impl for Cordova/iOS. Would fit nicely with the idea of libuv.js / happy to bring that work over here. |
👍 |
Happy to contribute the libuv.js code to this org (or whatever org we need) or y'all can start fresh if that is easier :) |
@defunctzombie can you link us to the repo :) |
I have a few feels to share about the following sentiment:
For a long time, I really enamored of the no.js approach, but The following things have since shaped my opinion greatly about this approach, and what the
voxeljsVoxelJS has a peer dependency problem. The immediate problem was the plugin structure: plugins accepted an entire engine instance as a parameter, creating a peer dependency on a specific version of the voxel-engine package. Sometimes this was explicit, other times not. The end result was that some voxel plugins were incompatible with each other. This hints at a theme: packages should only rely on shared, globally available primitives to communicate. The THREE problem was more insidious than the engine-as-parameter problem. Plugins (and even core components of voxel) would pull in THREE.js themselves. They depended on THREE for vector and matrix primitives. Often, these versions of THREE were incompatible, and would result in multiple copies of THREE manifesting in When THREE changed the signature for vector operators some plugins broke because Eventually, voxel moved to a three-element array-based vector system, where each package could bring in a copy of glmatrix -- data was communicated in a globally-available primitive, while operations were stored locally to the module. This was also (IIRC) the birth of ndarray -- separating operations from a globally-available backing store primitive. This approach to decoupling packages works, but only provided the operations can be extracted from the data and localized -- see the current state of Promise-based packages in the npm ecosystem for an example: the best practice is to present a callback-based API. This is not just to cater to a larger audience, I suspect -- it also effectively shields those package authors from promise implementation interop problems. Also note: Promises are extensively specified and are now global primitives to address this very issue. adding pipeline errors to streamsAs a result of a conversation with @phated, it came to my attention that gulp was looking for a way to "handle" errors as part of a stream pipeline. We sketched out a plan for something called "pipeline errors" -- which I then implemented. The feature itself (though stalled) is not the primary concern here: what I want to call attention to is the It's hard to make improvements on primitives that are loosed into the userland module ecosystem. If a package pegs their version of streams, and exports a stream instance that's used by another package that's using platform streams, there's no telling what bugs might arise when streams change in the future. Again, packages in the module ecosystem can only meaningfully communicate using globally available primitives -- and I would contend that it's the distribution's job to choose and tend to those primitives, because the module system precludes packages from usefully selecting these abstractions. core modulesFinally, what really moved me away from the no.js perspective was spending time with the core subsystems in Node. A lot of the "split out modules" discussion takes for granted that core modules are separate items that can be consumed piecemeal by userland. However, because the distribution defines global primitives, and those primitives are implemented in terms of each other, the core subsystems are deeply intertwined -- for the most part, they can't be consumed piecemeal. Streams are built on event emitters which use domains which are tied into the C++-level wrappers which themselves expose handles for streams to use. All of these use the concept of timers which are tied into domains and async listeners. The module system is built off of FS which is built off of the C++ fs wrapper, which is tied into domains / async-listener, which themselves are implemented as event emitters. There's potential to change some of this, yes -- or otherwise bootstrap it -- but I think the dissolution of core modules will look vastly different from how it's been proposed thus far, and the notion of using the module ecosystem to move the distribution forward is using a hammer where a saw is needed. Any distribution will need to pick shareable primitives for at least the following operations to expose libuv to JS:
I suspect that by picking those, in addition to exposing the libuv APIs, one will end up with something that looks very much like Node. That said, there's definitely value in keeping core slim. It would even be interesting to reimplement the above using the primitives WHATWG has developed to address the above needs (typed arrays, promises, and whatwg/streams)! Distributions are there to bootstrap the module system, and to do the things that the module system cannot. Picking cross-package primitives is the responsibility of the distribution -- I don't think streams, buffers, or event emitters can be pushed entirely into userland in a useful way. An aside:
I agree that there's a problem -- but I don't think it's the presence of C++ that's driving folks away from steering the project. Adopting something like the Rust RFC process, and ensuring that all technical decisions -- including ones from the TC -- go through that process would go a long way towards getting folks involved. |
Another note: this will probably affect distribution much for many people. I know that Voxer, for example, is rolling out just a node binary to some of it's servers to deploy stuff. |
I love this discussion. As I mentioned in #28 (probably off topic in that thread), I've been experimenting with this idea for some time and have successfully implemented it in luvit. I suggest to split the project into 3 layers.
There is a huge gap between what node currently offers and what libuv.js should offer. Writing libuv bindings for a new scripting engine is 70% figuring out how to best expose libuv in the semantics of the scripting language. The other 30% is pure grunt work applying what you decided to the full libuv feature set function by function. The remaining 90% is repeating steps 1 and 2 a few times and throwing away code till you're happy with it. Completely rewriting Rewriting luvit, on the other hand, has been full-time work for a few months for me and others and we're still not done. The middle layer (luvi in my project) helps tremendously by separating concerns. Luvi is in charge of bundling the various C bindings and statically linking them into the scripting engine and producing a single binary result. This binary has no require system and very little in the way of standard library. It does have one killer feature though. It can detect if a zip file is appended to it and can treat that embedded zip file as a virtual filesystem. It will automatically look in this embedded zip for This splitting does wonders for improving workflow when working on luvit core and eases contribution. You don't even need a C compiler installed on your system to built fresh new luvit binaries. One of my collaborators worked on luvit during thanksgiving while at his relatives house using the windows laptop in their house. He didn't need to install visual studio or anything, just install git and start working. |
Is anyone interested in any of these approaches or should this issue be closed? I think it is wise to make a decision here or try experimentation or close the issue. Lingering issues have a way of just going nowhere. |
I'm afraid I don't have a whole lot of time to do actual coding in node with this, but I am creating a new JS platform from scratch that will use the same technique I did for luvit. If node or iojs were to adopt my suggested approach it would mean basically rewriting the thing from scratch porting modules over one at a time to libuv.js. If libuv.js had a similar JS interface to my duktape bindings (and it should) then much of the higher-level sugar code could be reused between the projects. If done right, we could even have swappable js engines. I wonder if @brianleroux has time to get his JSC bindings to the same state or someone at mozilla wants to take another stab at my luvmonkey effort. I will continue my duktape project regardless. I'm only here to share my experience and opinion and offer assistance if someone decided to go this route. |
In short, everyone likes the idea in theory but there are a variety of concerns with what the actual implementation might look like. I'm going to close the issue in favor of a future PR or new repo with real code :) |
1. To avoid many warnings, this PR declares the C and C++ standards separately. 2. This PR extends gyp so that we can build with AVX-512. Nevertheless, getting runtime dispatching with ClangCl through Visual Studio is challenging, so we disable it. It only affects base64 and one component of zip, so the effect on runtime performance should be negligible. Note that other dependencies such as simdutf do not need to this build support for runtime dispatching (so you still get AVX2, AVX-512 support in these dependencies).
I'm going to coalesce as much as I can remember of dozens of conversations I've had with people over the last year in to an actual proposal.
For some time
node
has been getting separated in to difference projects which then become dependencies of node itself in some way. These range fromlibuv
toreadable-stream
(which is juststream
in core).How and when a module gets broken out in to its own project has been seemingly random, although a more accurate description might be "whenever the opportunity arrived," like a module rewrite.
A specific "version of node" must be a static collection of these dependencies, as has been the case for the history of node, suggesting that we change this would be crazy. Below I will describe this proposal as a collection of projects (some existing and some yet to be produced) which would be locked to a specific version of each project in order to produce a single version of
node
.v8
libuv
http-parser
libuv.js
-- libuv bindings to v8require
-- the module systemstdlib.js
-- every JS module you canrequire()
without installing as a dependencyreadable-stream
npm
node-bin
-- node's command line interfaceNote that there is no abstract C/C++ interface on top of v8's API. Native modules would still be built using a combination of
nan
(which has the advantage of being able to stretch multiple v8/node version combinations) and direct v8 bindings, as is the case today.The biggest benefits I see with this approach are reuse and ease of contributions. I think we would see some very interesting experiments with libuv.js. I also think that the community in userland that has had a hard time getting input in to node core would have an easier time directing that feedback towards a
stdlib.js
orrequire
project which is entirely JS.Concerns with this approach
The text was updated successfully, but these errors were encountered: