Meteor - Packaging

meteor

What is Isopack?

It is Meteor's own package format

Why does Meteor call its package format Isopack?

It is because of the isomorphic philosophy. In contrast to npm modules, Meteor's isopack aren't limited to the server, but they can include the server, browser, and even mobile code. For example, the http package, which provides HTTP functionality, use XMLHttpRequest in the browser, but on the server environment, it uses http.request.

What can be included in an Isopack?

Isopacks aren't limited to JavaScript code. They can also include style sheets, templates, static assets such as images and fonts.

What is the format for the version number used for Meteor packages?

MAJOR.MINOR.PATCH

When should we increase the major version number?

Increase this number if the package changes significantly and contains incompatible API changes. This signal to the developers using the package "if I update from version 1.x.x to 2.x.x, I have to be sure to adjust my codebase using the new API methods."

When should we increase the minor version number?

Increase the minor version number if new functionality is added but no breaking changes are expected.

When should we increase the patch version number?

Increase the patch version number for patches that fixes bugs but don't break any API.

Does Meteor support multiple version of the same package?

No. Unlike Node, Meteor only support a single version of a package per application. It's not possible to have different versions of a package like jquery or http installed. It can work on the server, but would lead to unpredictable behavior on the client side.

What are the three core packages that automatically installed when you create a project?

  1. meteor-platform: a collection of nearly 50 package libraries, including Tracker, Blaze, Minimongo, and jQuery
  2. autopublish
  3. insecure: allow database writes from the client

How can we see the list of packages that are included in the meteor-platform package?

meteor show meteor-platform

How can we see all the packages that are maintained by MDG?

meteor search --maintainer=mdg .

Notice the dot at the end.

Why do some packages have the mrt: prefix?

Some packages have an mrt: prefix, which indicates that they've been automatically migrated to the new packaging system that was introduced in Meteor 0.9.0. They may not be actively maintained anymore. Be careful when using those.

Why do some packages have the mdg: prefix?

These packages are the core packages maintained by Meteor Development Group.

How can we add the latest version of a package to our application?

Add it without specifying a version:

meteor add twbs:bootstrap

How can we add a specific version of a package to our application?

Append the @= operator:

meteor add twbs:bootstrap@=3.3.2

How can we specify a minimum version number when adding a package to our application?

Instead of using the @= operator, use the @ operator:

meteor add twbs:bootstrap@3.3.2

This will tell Meteor Version Solver to always use Twitter Bootstrap in version 3, but never below 3.3.2

Do we have to specify a version number when removing a package from our application?

No. Meteor only support one version of the package, so we do not have to specify a version number when removing a package.

meteor remove twbs:bootstrap

How can we use npm packages?

There are two ways to add an npm package to a project. The first is to wrap it into a Meteor package, which is usually the better approach. Most npm packages are designed to work only within the server context, so they don't follow the isomorphic nature of Isopacks. The second approach is to use the meteorhacks:npm package which allow you to use a packages.json similar to plain Node.js projects.

Writing an isomorphic wrapper for an npm module is outside the scope of this question. You have to do more reading. For now, we will use the meteorhacks:npm package. This package enhances Meteor applications so that npm modules can be used directly. Because the package needs to perform some configuration tasks before it can add modules, the project must be run using the 'meteor run' command after you add meteorhacks:npm. As a result, a new folder named packages will be added to the project. It contains an npm-container package that will take care of adding npm modules.

To specify which module you want to add to a project, you'll use a packages.json file. This file is also created when first running a Meteor project with the npm package added, and it's located at the root of the application folder. Example of this file:

{
  "gravatar": "1.1.1"
}

Adjust the contents of the packages.json file and restart Meteor, and the npm module will be added automatically. Because npm doesn't provide client functionality, modules are required from server-side code only using Meteor.npmRequire(). Once a module is loaded, it can be used in the same way you would use it in a plain Node application.

How can we create a local package?

Meteor doesn't yet support private package repositories, so all packages must either be published publicly or used locally within a package folder inside a project. This is where package development usually start.

Each package has a maintainer that is identified by as a prefix. Meteor developers can either use their own username or that of an organization (allowing multiple people to work on the same package). If you are a register Meteor developer, you should use your username as the prefix when creating a new package. If you have not registered for an account, you can do so on the meteor.com web site.

You have two options when deciding where to locate the new package: inside an existing application or outside. If you choose to create a new package inside an existing application, you must still add it via the 'meteor add' command. The cleanest solution is to create new packages outside of any application. To create a new package:

cd somewhereOutsideOfYourApplicationFolder
meteor create --package prefix:packageName

The above command creates a boilerplate file structure, including a README.md file. This basic structure assumes that all code goes inside a single JavaScript file. All unit tests should be in a dedicated *-tests.js file. Metadata, such as the package name, version, and dependencies should be in the package.js file. As with regular Meteor projects, there is no need to keep the given structure, the only mandatory file is package.js

The package.js file contain 3 important blocks (Package.describe, Package.onUse, and Package.onTest). See other entries on this page for information on these blocks. Example of a complete package:

Template.notificationArea.helpers({
  notification: function() {
    return Session.get('notify');
  }
});
Template.notificationArea.events({
  'click button': function() {
    Session.set('notify', '');
  }
});

Notification = {
  setError: function(text) {
    Session.set('notify', {
      type: 'error',
      text: text,
      buttonText: 'Oh, no.'
    });
  }
};

Because the 'Notification' object is automatically imported into your application, you can invoke the setError method from inside your application:

Notification.setError('...');

In the above code, we define the notificationArea template, its associated helpers, the Notification object and its method. Each package should only export one variable. In the CSS file, we can define whatever CSS rules we need for our package to work. Our template probably should use CSS classes that are named after the package, to reduce the likelihood that our CSS may have on the applications that are using our package.

Now that we have a fully functional package, but before we can use it in our application, we need to add it to our application. But Meteor expects local packages to be available in a packages directory within your project. If you created the package outside of your application, you can create a symlink in the filesystem, but that won't work well across different workstations. It's better to specify the location of local packages via the environment variable 'PACKAGE_DIRS'

What are the 3 important blocks of package.js?

The package.js file contains 3 important blocks:

  1. Package.describe(): The name and description of the package and the link to a Git repository
  2. Package.onUse(): The definition of the actual package, which Meteor API version to use, which files are used, and so forth
  3. Package.onTest(): The test definition for the package.

If a package relies on npm packages, a fourth block may 'Npm.depends()' is also used. For packages that use npm packages, you do not need to add the meteorhacks:npm package.

What are the ingredient of the Package.describe block?

  1. name: a unique name for the package, using a Meteor developer account or organization as a prefix
  2. version: a version number in the major.minor.patch format. When you use a hyphen, you can add pre-release information after the patch such as 1.1.0-rc1
  3. summary: a single line to be displayed when using the 'meteor search' command
  4. git: the URL to a Git repository containing the source code for the package
  5. documentation: the documentation file you want to use. It should be set to null if no documentation should be used.
  6. debugOnly: if set to true, this option will prevent the package from being included in the build bundles.

What does the Package.onUse section look like?

Package.onUse(function(api) {
  api.versionsFrom('1.1.0.2');
  api.use(
    [
      'templating',
      'ui'
    ],
    'client'
  );

  api.export('Notification','client');
  api.addFiles(
    [
      'notifications.html',
      'notifications.js',
      'notifications.css'
    ],
    'client'
  );
});

The second argument to api.use specifies the architecture, that is, server, client, web.browser, web.cordova. Even though packages are isomorphic, this allows for a leaner build output. If a package is required only on the server, the build process will not include it with the browser bundle, thus reducing the size of what needs to be sent via the network.

What is the purpose of the Package.onTest block?

By default, all packages are tested using the tinytest package. Therefore, it is the first dependency to be declared inside the onTest() block. The package under test must be declared as a dependency as well, even if it's the current package.

Package.onTest(function(api) {
  api.use('tinytest');
  api.use('prefix:packageName');
  api.addFiles('notifications-tests.js','client');
});

How can we declare npm dependency when developing our own package?

Npm.depends({package: 'version'});

This code will make an npm package available to the application. To use its functionality instead of using the plain Node syntax for 'require', simply prefix it with Npm:

variableName = Npm.require('packageName');

How can we test Isopacks using tinytest?

The tinytest package is designed to make testing packages as simple as possible. It comes with a nice web interface that present all test results as a glance, making it easy to run and analyze tests. Running package tests is done using the meteor CLI tool:

meteor test-packages

This will start Meteor application on localhost:3000 where you can see all the results of all tests. The tests run automatically if you can any of the tests, just like a regular Meteor application. If you want to develop your application and also want to have tinytest test reporter running at the same time, you can specify a different port for the test reporter:

meteor test-packages --port 4000

This way you can have the test reporter running on http://localhost:4000 and your normal Meteor application running on http://localhost:3000.

Tinytest.add('setError', function(test) {
  var msgText = 'An error message';
  Notification.setError(msgText);
  test.equal(Session.get('notify').text, msgText);
  test.equal(Session.get('notify').type, 'error');
});

The above code should go inside the notificiations-tests.js file. The first parameter for the Tinytest.add method is the name of the test. Tinytest exposes different test functions such as equal. If you want to structure your tests even more, you can use a hyphen in the name of your test. That way, you can group tests and get a better overview in the test report. This allows you to fold and expand groups of tests, which becomes especially important for larger packages.

Let's revisit the test setup defined in package.js:

Package.onTest(function(api) {
  api.use('tinytest');
  api.use('prefix:packageName');
  api.addFiles('notifications-tests.js','client');
});

The last line declares tests to run only on the client. If you change the line to:

api.addFiles('notifications-tests.js');

the tests are run in every environment the application is targeted to be built for. If the notifications tests are run on the server, they'll all fail because the Notification global is only exported to the client (we did that in the package.js file). The test reporter will show all tests, regardless of the platform they ran on. Each test is prefixed with either 'S' or 'C' to indicate whether it ran on the server or the client.

You can also specify a single test that should be run on the server and on the client as well. You should use tinytest to unit-test your packages, especially if you want to publish your packages to let other people use them.

Unfortunately, the tinytest package isn't documented very well, but most API calls are straightforward to use because they adhere to basic testing operations.

test.equal(actual, expected, message, not);
test.notEqual(actual, expected, message);
test.instanceOf(obj, class);
test.matches(actual, regexp, message);
test.isTrue(actual, msg)
test.isFalse(actual, msg)
test.isNull(actual, msg)
test.isNotNull(actual, msg)
test.isUndefined(actua, msg)
test.isNaN(actual, msg)
test.length(obj, expected_length, msg)

How can we publish our package?

Once a package is in a usable state and all tests pass, it can be published to the Meteor package infrastructure. New packages must also include the --create flag:

meteor publish --create

We might need to run the above command inside the 'packages' folder.

What is the process for updating a package?

  1. Make change and test your package locally
  2. Increment the version number in your package.js file
  3. Publish the update with the 'meteor publish' command

Can we unpublish a package?

No. There is no way to unpublish a package because we can't know for sure if someone is already using it. The only thing that comes close to unpublishing it or deleting it is to hide a package from the 'meteor search' command and the 'meteor show' command. You can do so by setting a package to 'unmigrated'. To do so, issue the following command inside the root folder of a package:

meteor admin set-unmigrated
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License