Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Writing Packages

Cristian Carlesso edited this page Jan 3, 2017 · 6 revisions

Code in Nuclide is organized as a collection of Node/Atom packages.

Creating a new Package

Code in Nuclide is organized as a collection of Node and Atom packages. These are realized as a set of module folders within pkg/nuclide and pkg/fb in the Nuclide project (for open-source, and Facebook-specific functionality respectively). Each has an entry point (probably main.js), and a package.json file to describe its purpose and APIs.

We have a handy script to create a new Node/Atom package:

# Creates an Atom or Node Package depending on your answers to the prompts.
~/Nuclide/scripts/create-package.py <package-name>

This will create ~/Nuclide/pkg/<package-name> and the associated files.

Developing a Package

In general, you can edit .js files in place and use ctrl-alt-cmd-l to reload Atom and test your changes.

Differences between Atom and Node Packages

(See Tools/Nuclide/pkg/README.md for a detailed explanation on this topic.)

Most of our packages should be treated as Node packages. Such packages can be require()'d directly. By comparison, Atom package activation order is unpredictable, so they do not make for good dependencies.

If you have some logic that needs to be import'able in an Atom package, just split it into two packages.

Basically, you should only create an Atom package if you are using an API that is unique to Atom packages, which includes:

  • One of the special Atom directories like keymaps, menus, styles, etc. (Though the nuclide-atom-npm often eliminates the need for an Atom package in this case.)
  • A "providedServices" or "consumedServices" entry in your package.json.

Note that what we call a Node package is not traditionally what you would see on npm. Specifically, our "Node" packages can assume the global variable, atom, is present, and can leverage things like "use babel".

The overarching reason we are doing things this way is that the npm community strongly prefers using many repositories, each of which uses semantic versioning, whereas we operate under the "one repo to rule them all" approach. One reason we prefer this approach is because we rarely have to commit version bumps, whereas a large portion of commits to Atom merely bump a version number of one of its dependencies. Instead, when we upgrade something, we fix all the call sites and everything moves forward together. This is why we symlink our folders rather than copy them.

Importing packages

Use the import statement at the top of your file. Do NOT use require. More info TBD.

Pattern for Main Files

It's common for an activate() function to initialize a number of variables local to the file and for deactivate() to set them to null. This doesn't play well with Flow because a bunch of fields that are non-null for the lifetime of the package have to be declared as nullable for Flow. A good way to work around this to create a class that contains all of the state for the lifetime of the package's activation, which makes it easier to create/tear down:

'use babel';
/* @flow */

import createPackage from '../../commons-atom/createPackage';
import UniversalDisposable from '../../commons-node/UniversalDisposable';

class Activation {
  _disposables: UniversalDisposable;

  constructor(state: ?Object) {
    // Assign all fields here so they are non-nullable for
    // the lifetime of Activation.
    this._disposables = new UniversalDisposable();
  }

  dispose(): void {
    this._disposables.dispose();
  }
}

export default createPackage(Activation);

Package Do-s and Don't-s

activate

Do not raise events in your Activation constructor or activate method. Listeners will not have had a chance to subscribe to your events until after your activate completes. If you are tempted to raise an event in your activate method, then you probably want to defer the work to PackageManager.onDidActivateInitialPackages.

In general, you should use the Services API to communicate across Atom packages. Or move logic out of an Atom package and into a Node package, making it easier to share.

serialize

Do not call atom.config.set from a package's serialize method. atom.config.set does not flush the write to disk, it only schedules the write for sometime in the next 100ms. During app shutdown your chance of getting your config to disk before the process terminates is small.

Checklist

package.json

  • Do begin name with 'nuclide-'
  • Do include a helpful description