Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(list): Extract @lerna/listable utility
  • Loading branch information
evocateur committed Jul 30, 2018
1 parent 41f231f commit bf56018
Show file tree
Hide file tree
Showing 12 changed files with 453 additions and 149 deletions.
27 changes: 2 additions & 25 deletions commands/list/command.js
@@ -1,6 +1,7 @@
"use strict";

const filterable = require("@lerna/filter-options");
const listable = require("@lerna/listable");

/**
* @see https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
Expand All @@ -12,31 +13,7 @@ exports.aliases = ["ls", "la", "ll"];
exports.describe = "List local packages";

exports.builder = yargs => {
yargs.options({
json: {
group: "Command Options:",
describe: "Show information as a JSON array",
type: "boolean",
},
a: {
group: "Command Options:",
describe: "Show private packages that are normally hidden",
type: "boolean",
alias: "all",
},
l: {
group: "Command Options:",
describe: "Show extended information",
type: "boolean",
alias: "long",
},
p: {
group: "Command Options:",
describe: "Show parseable output instead of columnified view",
type: "boolean",
alias: "parseable",
},
});
listable.options(yargs);

return filterable(yargs);
};
Expand Down
118 changes: 10 additions & 108 deletions commands/list/index.js
@@ -1,9 +1,7 @@
"use strict";

const chalk = require("chalk");
const columnify = require("columnify");
const path = require("path");
const Command = require("@lerna/command");
const listable = require("@lerna/listable");
const output = require("@lerna/output");

module.exports = factory;
Expand All @@ -18,114 +16,18 @@ class ListCommand extends Command {
}

initialize() {
const alias = this.options._[0];

this.showAll = alias === "la" || this.options.all;
this.showLong = alias === "la" || alias === "ll" || this.options.long;

this.resultList = this.showAll
? this.filteredPackages
: this.filteredPackages.filter(pkg => !pkg.private);

// logged after output
this.count = this.resultList.length;
this.result = listable.format(this.filteredPackages, this.options);
}

execute() {
let result;

if (this.options.json) {
result = this.formatJSON();
} else if (this.options.parseable) {
result = this.formatParseable();
} else {
result = this.formatColumns();
}

output(result);

this.logger.success("found", "%d %s", this.count, this.count === 1 ? "package" : "packages");
}

formatJSON() {
// explicit re-mapping exposes non-enumerable properties
const data = this.resultList.map(pkg => ({
name: pkg.name,
version: pkg.version,
private: pkg.private,
location: pkg.location,
}));

return JSON.stringify(data, null, 2);
}

formatParseable() {
const mapper = this.showLong
? pkg => {
const result = [pkg.location, pkg.name];

// sometimes the version is inexplicably missing?
if (pkg.version) {
result.push(pkg.version);
} else {
result.push("MISSING");
}

if (pkg.private) {
result.push("PRIVATE");
}

return result.join(":");
}
: pkg => pkg.location;

return this.resultList.map(mapper).join("\n");
}

formatColumns() {
const formattedResults = this.resultList.map(result => {
const formatted = {
name: result.name,
};

if (result.version) {
formatted.version = chalk.green(`v${result.version}`);
} else {
formatted.version = chalk.yellow("MISSING");
}

if (result.private) {
formatted.private = `(${chalk.red("PRIVATE")})`;
}

formatted.location = chalk.grey(path.relative(".", result.location));

return formatted;
});

return columnify(formattedResults, {
showHeaders: false,
columns: this.getColumnOrder(),
config: {
version: {
align: "right",
},
},
});
}

getColumnOrder() {
const columns = ["name"];

if (this.showLong) {
columns.push("version", "location");
}

if (this.showAll) {
columns.push("private");
}

return columns;
output(this.result.text);

this.logger.success(
"found",
"%d %s",
this.result.count,
this.result.count === 1 ? "package" : "packages"
);
}
}

Expand Down
5 changes: 2 additions & 3 deletions commands/list/package.json
Expand Up @@ -33,8 +33,7 @@
"dependencies": {
"@lerna/command": "file:../../core/command",
"@lerna/filter-options": "file:../../core/filter-options",
"@lerna/output": "file:../../utils/output",
"chalk": "^2.3.1",
"columnify": "^1.5.4"
"@lerna/listable": "file:../../utils/listable",
"@lerna/output": "file:../../utils/output"
}
}
21 changes: 9 additions & 12 deletions integration/lerna-ls.test.js
Expand Up @@ -2,7 +2,6 @@

const cliRunner = require("@lerna-test/cli-runner");
const initFixture = require("@lerna-test/init-fixture")(__dirname);
const multiLineTrimRight = require("@lerna-test/multi-line-trim-right");

// normalize temp directory paths in snapshots
expect.addSnapshotSerializer(require("@lerna-test/serialize-tempdir"));
Expand All @@ -12,14 +11,12 @@ let lerna;

beforeAll(async () => {
const cwd = await initFixture("lerna-ls");
const run = cliRunner(cwd);

// wrap runner to remove trailing whitespace added by columnify
lerna = (...args) => run(...args).then(result => multiLineTrimRight(result.stdout));
lerna = cliRunner(cwd);
});

test("lerna list", async () => {
const stdout = await lerna("list");
const { stdout } = await lerna("list");
expect(stdout).toMatchInlineSnapshot(`
package-1
@test/package-2
Expand All @@ -28,7 +25,7 @@ package-3
});

test("lerna ls", async () => {
const stdout = await lerna("ls");
const { stdout } = await lerna("ls");
expect(stdout).toMatchInlineSnapshot(`
package-1
@test/package-2
Expand All @@ -37,7 +34,7 @@ package-3
});

test("lerna ls --all", async () => {
const stdout = await lerna("ls", "--all");
const { stdout } = await lerna("ls", "--all");
expect(stdout).toMatchInlineSnapshot(`
package-1
@test/package-2
Expand All @@ -47,7 +44,7 @@ package-4 (PRIVATE)
});

test("lerna ls --long", async () => {
const stdout = await lerna("ls", "--long");
const { stdout } = await lerna("ls", "--long");
expect(stdout).toMatchInlineSnapshot(`
package-1 v1.0.0 packages/pkg-1
@test/package-2 v2.0.0 packages/pkg-2
Expand All @@ -56,7 +53,7 @@ package-3 MISSING packages/pkg-3
});

test("lerna ls --parseable", async () => {
const stdout = await lerna("ls", "--parseable");
const { stdout } = await lerna("ls", "--parseable");
expect(stdout).toMatchInlineSnapshot(`
<PROJECT_ROOT>/packages/pkg-1
<PROJECT_ROOT>/packages/pkg-2
Expand All @@ -65,7 +62,7 @@ test("lerna ls --parseable", async () => {
});

test("lerna ls --all --long --parseable", async () => {
const stdout = await lerna("ls", "-alp");
const { stdout } = await lerna("ls", "-alp");
expect(stdout).toMatchInlineSnapshot(`
<PROJECT_ROOT>/packages/pkg-1:package-1:1.0.0
<PROJECT_ROOT>/packages/pkg-2:@test/package-2:2.0.0
Expand All @@ -75,7 +72,7 @@ test("lerna ls --all --long --parseable", async () => {
});

test("lerna la", async () => {
const stdout = await lerna("la");
const { stdout } = await lerna("la");
expect(stdout).toMatchInlineSnapshot(`
package-1 v1.0.0 packages/pkg-1
@test/package-2 v2.0.0 packages/pkg-2
Expand All @@ -85,7 +82,7 @@ package-4 v4.0.0 packages/pkg-4 (PRIVATE)
});

test("lerna ll", async () => {
const stdout = await lerna("ll");
const { stdout } = await lerna("ll");
expect(stdout).toMatchInlineSnapshot(`
package-1 v1.0.0 packages/pkg-1
@test/package-2 v2.0.0 packages/pkg-2
Expand Down
8 changes: 7 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions utils/listable/README.md
@@ -0,0 +1,25 @@
# `@lerna/listable`

> Shared logic for listing package information
This is an internal package for [Lerna](https://github.com/lerna/lerna/#readme), YMMV.

## Usage

### `listable.format()`

```js
const listable = require('@lerna/listable');

const { text, count } = listable.format(packages, options);
```

### `listable.options()`

```js
const listable = require('@lerna/listable');

exports.builder = yargs => {
listable.options(yargs);
};
```

0 comments on commit bf56018

Please sign in to comment.