• Meteor
  • Tutorial

Introducing the Package System

Meteor has its own package system. And if you look in the Meteor source in the packages directory, you'll see that most of Meteor's functionality is actually implemented as just a series of packages. But it turns out we can also make our own packages within our projects. And in this episode, we'll do just that, and introduce the package system.

Packages can be a useful way for us to organize functionality in our applications. But one question you might ask is, why do we need a separate package system. Why can't we, for example, just use NPM.

Well as you'll see in a second, the Meteor package system lets us do some powerful things. For example, we could just load files on the client, we could load files on the server, or we can load them in both places. We can even load HTML templates, or CSS. And that's something that we can't accomplish with just NPM alone. But as you'll see, we can use NPM inside of our packages as dependencies. So let's get started by creating a package directory in our project.

I'll start by creating a directory called Packages, and Meteor will automatically look in this directory for any packages. So I'll change into the Packages directory. And I'll create my first package.

I'll just call it My Package for now. And then we'll change into that directory. Each package has to have at the very least a package.js file, which will tell Meteor about the package and what the package should do at bundle time.

I'm going to create a few files. One is going to be called client.js. This will be code that will load only in the browser. Something called both.js, and this code I want to load in both the browser, but I also want it to be available in the server.

And then let's create one more file called templates.html. And in this file, we'll put some custom templates that come with this package. And then we'll open our editor. And we'll start off with the package.js file.

You can think of the package.js file as sort of a configuration file that will configure our package for use in our application. And the first thing I'm going to do is describe the package. And so we have a name space available to us called package inside this file. And it has a describe method which takes an object as a parameter. And in that object, I'm going to give it a summary property that says this is, we'll just say, giving the package system a spin.

Next, if we wanted to, we could include NPM dependencies for the server. So for instance, if we wanted to use, say, the [? queue ?] job framework, I could specify the name of the NPM dependency as the key, and as the value, it's version. But for this episode, we're just going to keep it simple, and we're not going to use any NPM dependencies.

So next what I need to do is to tell Meteor what to do with this package. So I'll call the on [? use ?] method of the package. And that will take a function as a parameter that has an API as its parameter. Next, I can tell Meteor which packages that ship with Meteor or other packages in my project that this package depends on. And to do that, I can call a method of the API object called use.

And then as a first parameter, I can pass a string or an array of strings. And I'm going to pass the names of the packages that this one depends on. And so for example, we may want to use the underscore library. And additionally, since we're going to have HTML templates, we need to rely on the templating package. And we want these libraries to be-- these other packages to be available on the client in this case.

The next thing I'm going to do is to say which files need to be added, and where. On the client, the server, or both. And so let's start with the server. I'll call the Add Files method. And we only have one file called both.js, which we want loaded on the server and not the client. And then I'll call api.addfiles again. And in this case, I have a few files that I want available only on the client.

So the first one is our templates.html file. And the second one is our client.js file. And I want those files only available on the client, or only loaded on the client.

And actually, in our both.js case, I want this available on the client and the server. So I'm just going to change the second parameter to an array and say that I want it on client and the server. OK, so our package is all set up at this point. And this file will tell Meteor what to do. Namely, we're going to use the underscore and templating packages, and then we're going to add the both.js file on the client and the server. And our templates.html and client.js files will only be loaded onto the client.

And so let's go and fill out some functionality for our package. I'm going to start off with both.js. And this will be the JavaScript that will be available on the client and the server. Let's say that I wanted to create a method off of the string prototype to truncate strings. And so I'm going to create a method off of the string prototype called Truncate. And it will just take some options as a parameter.

And as you can guess, what this method will do is it will take a string and return a truncated version of it up to some maximum length. And so let's create a variable called length. And that will be equal to the options length, if it's provided, or we'll just default it to 50 characters.

Next, we'll create an ending variable that again will be equal to the options ending. And if none is provided, we'll just default it to ellipses. And then let's create our truncated string, which will be this.slice [INAUDIBLE] the length. And remember, this is just our current string.

And then we'll see if the truncated length is less than the current length. We'll add our ending. And then return the truncated string.

OK, so that code will be loaded on the client and the server. But now let's go into our client.js file and register our Handlebars helper. So I'll call the Register Helper method, and we'll call this Truncate. And it'll take a string as the first parameter, and the normal options that we get in our helpers as the second parameter.

And we'll create a variable called truncated options. And if you're not familiar with Handlebars, what's going to happen here is the Options object will get passed automatically into this method. And if it has a hash, those will be the options that are intended to be passed along. So you can check out the Handlebars episodes if you're not familiar with that.

And then what we'll do here is we'll just call the Truncate method on the string, and pass the truncated options as a parameter. And we'll just guard this and set it equal to an empty object, if none exist. And so now this code will only be loaded on the client. And so we'll have a Truncate helper that we can use in our templates.

Last, let's go into the templates.html file and define a template that we can use with this package. So I'll call this template Sample Truncated String. And we'll use the new helper that we defined, called Truncate. And then we'll pass our options, and we'll say that the length is equal to, say, 10, and the ending is equal to the three dots.

OK, so back in our regular project directory, you can see the app.css, HTML, and js files, and then our newly-created Packages directory. So Meteor will automatically load packages that are in this directory, and make them available for our application, depending upon each package's package.js configuration. So let's jump into our app at this point and go into the app.html file.

And inside there, we'll try rendering our sample truncated template. And actually, I believe I named it Sample Truncated String. So we'll just change that.

And we'll fire up our Meteor server and jump over to the browser and see if it worked. OK, great. So in the browser, we can see that things are working properly. And in our JavaScript console, we don't see any errors.

And just as one more example, we can use this Truncate helper directly if we want to. So let's just try it again. And for a length, I'll pass 10. And that worked, as well.

So a couple of concluding thoughts. One is we can see how the Meteor package system lets us load functionality on the client, on the server, or both, and also allows us to depend on NPM modules if we need them. You might have used the Meteorite package system, which allows us to publish packages into the open, and then other people to pull those packages into their project. Meteor doesn't have this yet. So in the meantime, you can use Meteorite packages, or if you want you can also just load packages or create packages directly into your Application directory.

But hopefully, this episode gives you a sense of where the package system is going. A final note is that the package system currently is undocumented. And so we can expect some changes in this API. But, I think that this reveals how powerful this system can be. And we'll be using the package system quite a bit in future episodes.