Your strategies to avoid collisions in the global namespace?

IMHO, Meteor’s approach of controlling the global JavaScript namespace is sub-optimal, and I would love to see the ES6 module syntax supported ASAP. But it’s not there yet, so I have to deal with it somehow.

When I started developing my Meteor apps, I put every model object in a central namespace called “AppMain”, just to avoid collisions with other packages. I used this generic name instead of the application’s name, since I wanted to keep my model objects reusable.

But this approach soon got very ugly, since I had to prefix every model object with “AppMain.” if I wanted to reference it. I had the feeling that I was the only one doing this, since most community-packages I saw had no shame to put at least their central model object classes into the global namespace.

So I changed this for my app as well and dropped my special namespace.

Today, I had the first namespace collision, though, since both raix:push and my app were using the name PushNotification (which was not obvious, because the package used it internally only, but my own model class broke this). This was not easy to track down, since these internal classes could not be seen “on the surface”.

I now tend to revert to my initial approach, i.e. using namespace prefixing. Before I do this, I would like to ask the community what strategies you are using to avoid namespace collisions? Maybe there’s an approach I haven’t thought of yet.

@sashko: Is there already a chapter in the Meteor Guide for this topic? I could not access the guide since the server seemed to be down.

Hi @waldgeist!

We’re going to be targeting the new import/export API in the Guide if possible. We’re planning on releasing the guide alongside Meteor 1.3, and I’m pretty sure using modules will be the right choice once they are out. But that doesn’t help you much.

I think you can simulate a module-like approach with your app namespace idea like this:

const { PushNotification } = AppMain;

This uses the ES2015 destructuring assignment syntax, which is similar to how imports work:

// Note that this isn't the right path - I don't exactly know what it will look
// like in Meteor 1.3 release
import PushNotification from 'app/push-notification';

I think using the pattern from the first code snippet will set you up for a quick and easy migration onto modules!

What do you think?

2 Likes

Thanks for your reply! This destructuring approach looks interesting, I’ve never seen that syntax before. Does

const { PushNotification } = AppMain;

mean that the parser references the PushNotification key inside AppMain?

Yep, this is roughly the same as:

const PushNotification = AppMain.PushNotification;

This was a great tip, thanks a lot!

I changed the approach a bit now. I am using a global namespace variable “app”, plus a package-specific sub-namespace. So I can have, for instance:

const { PushNotification, NotificationManager }  = app.notifications;

To make this work, each package that is using the notifications sub-namespace contains a namespaces.js file which sets up the namespace if it has not been defined yet:

app.notifications = app.notifications || {}

This approach makes it easier to track in which package the model objects are actually defined in. I did not do this in my initial approach, since this made the syntax too cluttery. But with the deconstruction syntax, this is much better. This is the closest I got to the Java package import syntax so far. Thanks again for this idea!

1 Like

Yep, can’t wait until we can throw all this away and just say import :]

Oh yes, definitely looking forward to this! :slight_smile:

1 Like

I just noticed that this approach has a huge drawback:

Now the load-order of the files becomes very important, at least if you include the deconstruction syntax in the global part of your JS files. I had a collection schema file that was referencing a model class (to initialize values to their defaults using the model) and the same model file was using the collection schema. This worked as long as both the collection and the model were global objects. But switched it over to the deconstruction pseudo-include syntax, I had a deadlock between the two. Of course this only happens if you use the syntax inside the package to cross-reference other objects. There’s no problem if you just use it to reference values from outside the package.

As a solution, once can define the objects as globals throughout the package, but then mimick an export in a central export file, that puts everything together into the central app object.

Another update on this: After fiddling around with the deconstruction thing for a while, it turned out to be very error-prone, even if you use it on the application level.

The reason is that the

const {
  ModelClass
} = namespace;

thing only makes sense if you are using it in the global part of your JavaScript files (otherwise, there would not be much benefit). But if you include it in the global part, you have to be sure that the ModelClasses have been already registered to the namespace. If these classes are located in packages, it is nearly impossible to achieve this without causing circular dependencies of the packages over time. Also maintaining these dependencies becomes a nightmare.

So I cannot really recommend this approach, although it looked quite nice at first sight.