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
Proposal: split embark into embark and @embark/core #1048
Comments
One thing not mentioned in the write-up above is that if a DApp were to specify a dependency that itself had a peer dependency incompatible with one of |
Thx for making that great idea something we can all discuss formally about.
From there you can run:
I think that would reduce the number of case we have to handle and help the user to follow not only embark convention but also JS convention. |
Really really nice to see this discussion here. Let me throw in my thoughts as well:
@michaelsbradleyjr When would a Dapp ever try to resolve a module from Embark's node modules? The only thing I can think of right now, is that a Dapp wants to import from Embark (but not its node modules). For example, something I've mentioned above:
But also in this case,
I'd love that. I still don't fully get why we maintain another set of versions inside the dapp. So bottom line: I think the most straight forward, from a developer experience point of view, is if you have to only care about one thing: you either have That's how Angular CLI does it as well, I'm sure we can get some inspiration from their code base. |
I don't really agree with this. It feels premature given other stuff we still need to do, overcomplicates things possibly (although I might be misunderstanding..), but more importantly in the grand scheme of things we have other priorities first. |
Later today I can provide a more thorough reply to all the points raised so far. To @iurimatias's point, here's the problem: The cli shim that's landing in embark v4 is supposed to make it painless to have embark installed locally or globally, or both, and have everything always work regardless of a DApp author's preferred setup. The shim does work well, when it comes to embark commands. It's only recently become clearer to me that there are still serious challenges regarding module resolution. Our use of This proposal is a way to move forward with a setup that we know will work correctly, so long as we're careful about specifying some dependencies as peer dependencies (now, and in the future as embark evolves). It also has the benefit of laying the groundwork for breaking embark up into a set of smaller library modules. That should help make embark more flexible, allowing the components to be used in different ways, e.g. with alternative cli interfaces. Some community members have been asking about that, I think... I'm recalling discussions about angular cli and embark. |
@alaibe good proposals! Here are some ideas that build on yours: Instead of Rather than have an Importantly, none of the individual modules would directly expose anything to the command-line, i.e. no More concretely...
When the The I'm sure that all sounds complex, but here are the key benefits:
|
@PascalPrecht agreed on nearly all points re: namespacing. See my previous comment, will definitely appreciate some feedback. A big gain of the approach I outlined is that it would be possible to do I think keeping
^ that will simply offer what he wants inside or outside a DApp, regardless of whether |
@PascalPrecht to answer your question about resolving modules: In the Embark 3.1.x and 3.2.x series and in the 4 alphas (not sure about <=3.0.x), DApps might include a Starting with 3.2.x, things got more complicated with the ejectable webpack config, which expects Embark to provide it with the [+] "mostly", because even a non-ejected config can have problems if a DApp is using a babel plugin that needs While the Another case when a DApp can try to indirectly resolve a dependency from Embark's If we step back and consider the bigger picture, all of these connections can be better expressed as peer dependency relationships between a DApp and Embark. For example, That will work great, but we'll need both |
Overview
TL;DR
Module resolution with DApps and Embark is presently complex. See #1039, for example. If the bulk of Embark were contained in a locally installed
@embark/core
package, and if we make use of peer dependencies, the situation can be greatly improved. Later on, we might split up@embark/core
into a collection of locally installed@embark/[name]
packages, and that will work fine too.Extra Detail
Key ideas
embark
becomes a very lightweight package that can be installed globally or locally, and which provides a minimal cli.@embark/core
provides most of whatembark
provides now, minus the pieces in the revisedembark
.minimal
embark
cliIt would have:
@embark/core
, hooking into it where it finds it, or prompting the user to install it if wasn't found. It could even offer to install it for the user and update the DApp'spackage.json
.When run outside a DApp directory, the help output might look like this:
When run inside a DApp directory, it will bolt onto
@embark/core
'scmd.js
and the help output would show the additional cli facilities that@embark/core
provides (everything thatembark help
displays at the present time). Also, version output inside a DApp would include not only the version ofembark
but also of@embark/core
.@embark/core
It would consist of most of what's in
embark
presently, minus the parts that are in the revised package, such asbin/embark
, the template generator, etc. (see the previous section).It would be intended to always and only be installed locally. If it's installed globally, it won't cause any problems, but it would be inert and not usable by DApps. It's the same situation if you installed the
bluebird
package globally (npm i -g bluebird
) and then want to use it from a Node project you're developing: it doesn't work that way; you need tonpm i bluebird
inside your project.An important change is that any Embark dependencies that may be resolved by a DApp script or DApp dependency will need to be either:
@embark/core
and installed alongside it. The revisedembark
cli-package can help with this — after finding/installing@embark/core
it can check whether any peer dependencies specified by@embark/core
are not yet installed or if the already-installed versions aren't acceptable.@babel/core
is an example of a package that would become an@embark/core
peer dependency.require
an encapsulating module within embark. An example of a package that could be encapsulated inside an embark module islodash.clonedeep
. Assuming the DApp script is run in the embark process or a child process, then something like this could be done:const {cloneDeep} = require(path.join(process.env.EMBARK_PATH, 'dist/lib/core/utils'))
.Why am I proposing this?
If you're not 100% sure how module resolution works in Node, please read the docs.
There are presently three+ distinct
node_modules
trees in play between a DApp and Embark:A/embark/node_modules
B/dapp([/..]*)/node_modules
B/dapp/.embark/versions/*/*
(3) isn't really a
node_modules
tree per se, but it works like one. (2) could be a singlenode_modules
tree or a forest of trees that will be searched from inside out.Depending on what's installed where,
A
might be in a subdir relationship withB
or vice versa, or they may be the same or disjoint.Presently, when a DApp script or dependency needs to resolve a module from Embark's
node_modules
, we ensure that can happen by modifyingNODE_PATH
, and in some cases we also have to use a "custom require" approach since our runtime modification ofNODE_PATH
can't take effect in embark's main process (only child processes).These solutions work but are brittle and can have surprising results. And since they're implemented at runtime, npm may report a DApp is missing dependencies even though Embark can be expected to supply them.
How does this proposal help?
With
@embark/core
always in the DApp'snode_modules
tree/forest, and with select dependencies specified and installed as peer dependencies at the top-level of the tree in which@embark/core
resides, we eliminate the need forNODE_PATH
and "custom require", and we can be certain that Embark and the DApp will be able to resolve the package versions they need.This same approach should also let us confidently deprecate Embark's library manager, and therefore eliminate (3) in the list above. Any package that might be specified in a DApp's
embark.json
with a non-default version could be specified as a peer dependency of@embark/core
with a suitable semver range , allowing a DApp developer the freedom to specify the non-default version he wants in the DApp'spackage.json
and to depend on npm to install it in the usual way.The reason that it's critical for us to switch some packages to being peer dependencies is that transitive dependencies (if we moved from shrinkwrap back to package-lock for
@embark/core
) can be unpredictable. For one, there can be semver shenanigans with dependencies' dependencies when@embark/core
is installed from the registry. Also, there's no guarantee that some other dependency of a DApp (or the DApp itself) won't specify an incompatible version of a dependency also specified by@embark/core
— npm might (or might not) dedupe the incompatible version into the top-levelnode_modules
of the DApp, and then DApp scripts (such as an ejected webpack config) and DApp dependencies might end up broken at runtime.Note that the reason we'd need to switch from shrinkwrap back to package-lock to take advantage of
@embark/core
's dependencies as transitive dependencies is that shrinkwrap will keep all of them indapp/node_modules/@embark/core/node_modules
such that normal module lookup from within the DApp can't succeed... and so we'd be right back to needingNODE_PATH
and "custom require". 😱The text was updated successfully, but these errors were encountered: