Lerna: Node.js API documentation

Created on 27 Mar 2019  路  3Comments  路  Source: lerna/lerna

Expected Behavior

I would like to see show how lerna commands (and their output) can be used through their Node.js API.

Current Behavior

You need to read the tests (which I don't mind) and find out how commands are wrapped within these tests to understand how you can use them as a consuming developers.

Example:

Possible Solution

Markdown files with little snippets for common use cases, i.e. viewing all your changed packages. These snippets could be listed within the README.md of the individual command. For example here: https://github.com/lerna/lerna/blob/v3.13.1/commands/changed/README.md

Use case

I want to query the names of all packages which have changed since the last tagged releases. For this I am using the lerna changed command and it's CLI output:

const {execSync} = require('child_process');

let output;

try {
  output = execSync(`npx lerna changed --all --json`);
} catch (error) {
  console.info(`No local packages have changed since the last tagged releases.`);
  process.exit(0);
}

const changedPackages = JSON.parse(output.toString());
const packageNames = changedPackages.map(project => project.name);

console.info(`Changed packages: ${packageNames.join(',')}`);

I would prefer using lerna's Node.js API and drafted this:

const {ChangedCommand} = require('@lerna/changed');
const output = require("@lerna/output");

const command = new ChangedCommand({
  argv: process.cwd()
});

command.initialize();
command.execute().then(() => {
  console.log('Output', output.logged());
});

However, this ends up in the following error:

TypeError: Cannot read property 'rawPackageList' of undefined
at ChangedCommand.initialize (D:\dev\projects\wireapp\wire-web-packagesnode_modules\@lerna\changed\index.js:22:25)
at Object. (D:\dev\projects\wireapp\wire-web-packages\bin\testUpdated.js:28:9)
at Module._compile (internal/modules/cjs/loader.js:689:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
at startup (internal/bootstrap/node.js:266:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)

Your Environment

| Executable | Version |
| ---: | :--- |
| lerna --version | 3.13.1|
| npm --version | 6.4.1 |
| yarn --version | 1.12.3 |
| node --version | 10.9.0 |

help wanted

Most helpful comment

Yeah, sorry about this general lack of docs. It's mostly a failure on my part to factor the modules properly, and also the fact that I suck at writing documentation. Here's the guts of the changed command (ignoring the largely cosmetic listable.format stuff):

#!/usr/bin/env node

"use strict";

const { getPackages } = require("@lerna/project");
const PackageGraph = require("@lerna/package-graph");
const collectUpdates = require("@lerna/collect-updates");

(async function main() {
  const cwd = process.cwd();
  const pkgs = await getPackages(cwd);
  const graph = new PackageGraph(pkgs);
  const opts = {
    // The semver bump keyword (patch/minor/major) or explicit version used
    bump: "",

    // Whether or not to use a "nightly" range (`ref^..ref`) for commits
    canary: false,

    // Force all packages to be versioned with `true`, or pass a
    // list of globs that match package names you wish to force
    // (this option is not explicitly documented because it often indicates an anti-pattern)
    forcePublish: false,

    // A list of globs that match files/directories whose changes
    // should not be considered when identifying changed packages
    ignoreChanges: [],

    // Whether or not to include the --first-parent flag when calling `git describe`
    // (awkwardly, pass `true` to _omit_ the flag, the default is to include it)
    includeMergedTags: false,

    // Only include packages that have been updated since the specified [ref] "since".
    // If no ref is passed, it defaults to the most recent annotated tag.
    since: null,
  };
  const changed = collectUpdates(pkgs, graph, { cwd }, opts);

  changed.forEach(node => {
    console.log(node.name);
    // e.g., 'leaf-pkg'

    console.log(node.location);
    // e.g., '/absolute/path/to/repo-root/packages/leaf-pkg'

    // node.pkg is a Package instance, Lerna's internal representation of the leaf package
    // @see https://github.com/lerna/lerna/blob/master/core/package/index.js

    // get the raw package.json like this
    const json = node.pkg.toJSON();
    console.log(JSON.stringify(json, null, 2));
  });
})();

The (biggest) current problem with the command superclass is that it does too much magic, such that instantiating ChangedCommand like you did doesn't quite do the whole enchilada.

If you have ideas about exposing this in a more fluent API, I'm all ears.

All 3 comments

Yeah, sorry about this general lack of docs. It's mostly a failure on my part to factor the modules properly, and also the fact that I suck at writing documentation. Here's the guts of the changed command (ignoring the largely cosmetic listable.format stuff):

#!/usr/bin/env node

"use strict";

const { getPackages } = require("@lerna/project");
const PackageGraph = require("@lerna/package-graph");
const collectUpdates = require("@lerna/collect-updates");

(async function main() {
  const cwd = process.cwd();
  const pkgs = await getPackages(cwd);
  const graph = new PackageGraph(pkgs);
  const opts = {
    // The semver bump keyword (patch/minor/major) or explicit version used
    bump: "",

    // Whether or not to use a "nightly" range (`ref^..ref`) for commits
    canary: false,

    // Force all packages to be versioned with `true`, or pass a
    // list of globs that match package names you wish to force
    // (this option is not explicitly documented because it often indicates an anti-pattern)
    forcePublish: false,

    // A list of globs that match files/directories whose changes
    // should not be considered when identifying changed packages
    ignoreChanges: [],

    // Whether or not to include the --first-parent flag when calling `git describe`
    // (awkwardly, pass `true` to _omit_ the flag, the default is to include it)
    includeMergedTags: false,

    // Only include packages that have been updated since the specified [ref] "since".
    // If no ref is passed, it defaults to the most recent annotated tag.
    since: null,
  };
  const changed = collectUpdates(pkgs, graph, { cwd }, opts);

  changed.forEach(node => {
    console.log(node.name);
    // e.g., 'leaf-pkg'

    console.log(node.location);
    // e.g., '/absolute/path/to/repo-root/packages/leaf-pkg'

    // node.pkg is a Package instance, Lerna's internal representation of the leaf package
    // @see https://github.com/lerna/lerna/blob/master/core/package/index.js

    // get the raw package.json like this
    const json = node.pkg.toJSON();
    console.log(JSON.stringify(json, null, 2));
  });
})();

The (biggest) current problem with the command superclass is that it does too much magic, such that instantiating ChangedCommand like you did doesn't quite do the whole enchilada.

If you have ideas about exposing this in a more fluent API, I'm all ears.

How would this work if I need to list all packages, not just changed?

@arvigeus Even easier, skip the call to collectUpdates and iterate over the result of await getPackages(cwd) (the guts of lerna ls, basically).

Was this page helpful?
0 / 5 - 0 ratings