-
Notifications
You must be signed in to change notification settings - Fork 682
Writing Packages
Code in Nuclide is organized as a collection of Node/Atom packages.
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.
In general, you can edit .js
files in place and use ctrl-alt-cmd-l
to reload Atom and test your changes.
(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 thenuclide-atom-npm
often eliminates the need for an Atom package in this case.) - A
"providedServices"
or"consumedServices"
entry in yourpackage.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.
Use the import
statement at the top of your file. Do NOT use require
. More info TBD.
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);
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.
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.
package.json
- Do begin
name
with 'nuclide-' - Do include a helpful
description