Question reguarding loading modules

First, let me say I really like this “community”.

We are are currently “modernizing” a current application. We want to make the application as modular as possible. One question I have is module dependencies. We have installed the ‘str’ module in the app. I am adding QuickORM to our app. ‘str’ is a dependency of QuickORM so it downloads with QuickORM. My question is that is there an issue with having 2 copies of the ‘str’ module. Is this really only a development issue? What about other modules that have dependencies that you already have in your app? Want I’m worries about is multiple copies of the same module, being deployed to production. What are some ways I can combat this (if it actually an issue)?

Good question. There’s a few scenarios that can happen:

If the installed dependencies match a version previously installed, then CommandBox will skip installing it. Here’s an example image:

However, if the versions don’t match, both will be installed.

At this point, you’re kind of in a bind. ColdBox can only load one module with the same name at a time. It is not possible for you to choose which one gets loaded or to have a modules load its nested dependencies. So this can cause some hard to diagnose bugs if you get different versions depending on the order CommandBox installed the modules or the order ColdBox loaded them. For this reason, using multiple copies of different major versions of a package in your application is risky. (It may be that the breaking change doesn’t affect you, but it’s a risk none the less.)

Note that you will get multiple copies of the dependency not only for different major versions, but different versions at all.


This is less of a risk, but the same caveats as above about installation and load order apply.

To answer your original question about is it an issue that both are on disk, the answer is maybe. Disk space wise, probably not. Application execution wise, maybe. Your safest course of action is to sync the versions of your dependencies to keep them all in sync.

@marmcr What you have described is not really an issue to worry about. A dependency should be a “black box” to your app. So if you install quick and it needs other modules, those are concerns for the package manager to worry about. It will take care of making sure they are installed one way or another. Since installing, updating, and removing modules is all automated by the CLI, you don’t need to worry about what is in installed and where.

That said, here are a couple caveat/behaviors to be aware of.

If you have the same module installed in more than one place, the ColdBox framework will only load the first one it comes across into memory since modules are loaded globally. Generally this is no issue, so long as all the uses of that module are a compatible version.

If you install a module at the top level of your app, and then install a second module that also depends on the module installed at your top level, CommandBox will optimize the installation and skip installing the nested module that is already there at the top level. This only happens when a duplicate module is installed “higher” in the app. If you install two top level modules that both depend on the same nested module, CommandBox will not try and optimize that since dependencies are a black box and if one were removed, the other would still need to work.

Small point of clarification. It’s not an == sort of match, the installation of the nested module will be skipped if the module at the top level satisfies the version requested for the nested module based on the rules of semver ranges. So if the top level module was version 1.2.3 and the nested dep asked for a range of 1.x.x then it would be considered a satisfying version. However if the nested version was pinned specifically to 1.2.0 then it would not satisfy. And then you’d be at the mercy of which module ColdBox decided to load first (assuming the exact version was a hard requirement)

That’s not quite what happened above. The top level installed 1.0.0 and the nested module wanted ^1.0.0. The latest version was 1.7.3 (or something like that). Both were installed.

You’re right. This is a bug and a regression. It seems some refactoring at some point changed the contents of a variable so this line

if( semanticVersion.satisfies( candidateBoxJSON.version, version ) ) {

was mistakenly using the exact version of the incoming package, not the semver range that was asked for which is why it was only finding exact matches. I’ve fixed this on the bleeding edge of CommandBox:

   | √ | Installing package [forgebox:str@^1.0.0]
   |   |-------------------------------------------------------
   |   | Verifying package 'str' in forgebox, please wait...
   |   | Installing version [1.0.7].
   |   | Verified entry in forgebox: 'str'
   |   | Package found in local artifacts!
   |   | Decompressing...
   |   | str (^1.0.0) is already satisfied by C:\sandbox\vertest\modules\str\ (1.0.0).  Skipping installation.
1 Like

I like the info message about already being satisfied. :+1:

FWIW, the info message has always been there. You can see it when you use --verbose on your install command. Of course, the message wasn’t appearing in this case since the logic wasn’t working correctly.

@elpete @bdw429s Thanks for the explanation. This is very helpful.

I assume this means that when a module is installed, it is checking the app’s box.json file to determine if the module exists and uses that version information? Does it traverse through other nested modules? I’m thinking of something like cbjavaloader. It also looks to be installed in different places. From above and my understanding, it would not check cbjavaloader as box.json at app level would not be tracking it.

I think what is also happening that my box.json for this app is not complete. It did not have an entry for the str module. This is a legacy app and I(we) have just started to work with CommandBox to manage the app.

I’ll test some more using the bleeding edge.

Thanks again.

Byron

Close, it checks every directory above until it reaches the root app and looks for a satisfying version of the module at any higher level

Remember, CommandBox will only look “up” the tree to see if a dependency is installed at a higher level. It won’t look “down” into other modules like I said in my first post.

I’m not really sure what you’re trying to say there. At some point, the root app’s box.json would be checked to see if javaloader was installed at the top level. Whether or not it finds it… I have no idea what you have installed! If you aren’t sure what you have installed, use the list command to find out.

list

Yes, if you bypass your package manager and start manually installing stuff on the side without it knowing, then it can’t do its job. The box.json is the definitive list of what is and isn’t installed as far as your package manager is concerned.