Skip to content

Commit

Permalink
Feature: Run command (#42)
Browse files Browse the repository at this point in the history
* wip for run

* fixes and refactor

* fixed a bug and added tests

* docs

* Update pink-rabbits-punch.md

* Update README.md

* Update README.md

Co-authored-by: Mitchell Hamilton <[email protected]>
  • Loading branch information
tarang9211 and emmatown authored Feb 14, 2020
1 parent 142b45d commit 63cdae1
Show file tree
Hide file tree
Showing 14 changed files with 276 additions and 11 deletions.
24 changes: 24 additions & 0 deletions .changeset/pink-rabbits-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
"@manypkg/cli": minor
---

Added `manypkg run <partial package name or directory> <script>` which can be used to execute scripts for packages within a monorepo.

As an example, let's say there are two packages: `@project/package-a` at `packages/pkg-a` and `@project/package-b` at `packages/pkg-a` which both have a `start` script, `manypkg run` can be used like this:

```bash
yarn manypkg run pkg-a start
yarn manypkg run a start
yarn manypkg run package-a start
yarn manypkg run @project/package-a start
yarn manypkg run packages/pkg-a start
yarn manypkg run package-b start
yarn manypkg run b start
```

The following wouldn't work though because the `package` and `pkg` aren't unique among the package names/directories:

```bash
yarn manypkg run package start
yarn manypkg run pkg start
```
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,29 @@ yarn add @manypkg/cli

`manypkg check` runs all of the [checks](#checks) against your repo and fixes any of problems that can be fixed.

### `manypkg run <partial package name or directory> <script>`

`manypkg run` executes scripts for packages within a monorepo.

As an example, let's say there are two packages: `@project/package-a` at `packages/pkg-a` and `@project/package-b` at `packages/pkg-a` which both have a `start` script, `manypkg run` can be used like this:

```bash
yarn manypkg run pkg-a start
yarn manypkg run a start
yarn manypkg run package-a start
yarn manypkg run @project/package-a start
yarn manypkg run packages/pkg-a start
yarn manypkg run package-b start
yarn manypkg run b start
```

The following wouldn't work though because the `package` and `pkg` aren't unique among the package names/directories:

```bash
yarn manypkg run package start
yarn manypkg run pkg start
```

## Dictionary

- **private package** - A package that has `private: true`/is not published. It does not refer to a package published to a private registry here.
Expand Down
7 changes: 7 additions & 0 deletions __fixtures__/basic-with-scripts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@manypkg/basic-fixture",
"version": "1.0.0",
"workspaces": [
"packages/*"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@manypkg/basic-fixture-pkg-one",
"version": "1.0.0",
"scripts": {
"start": "echo hello start",
"test": "echo testing"
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@manypkg/basic-fixture-pkg-two",
"version": "1.0.0",
"scripts": {
"start": "echo hello start",
"test": "echo testing"
}
}
Empty file.
37 changes: 28 additions & 9 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# manypkg
# Manypkg

`manypkg` is a CLI to make working in monorepos easier based on some of my experiences working in monorepos, conversations with other people and lots of learnings from `bolt` and `bolt-check`.
Manypkg is a linter for `package.json` files in Yarn, Bolt or pnpm monorepos.

## Install

Expand All @@ -10,14 +10,33 @@ yarn add @manypkg/cli

## Usage

### Checker
### `manypkg check`

```
yarn manypkg check
```
`manypkg check` runs all of the [checks](#checks) against your repo, logs any errors and exits with a code

```
yarn manypkg fix
### `manypkg fix`

`manypkg check` runs all of the [checks](#checks) against your repo and fixes any of problems that can be fixed.

### `manypkg run <partial package name or directory> <script>`

`manypkg run` executes scripts for packages within a monorepo.

As an example, let's say there are two packages: `@project/package-a` at `packages/pkg-a` and `@project/package-b` at `packages/pkg-a` which both have a `start` script, `manypkg run` can be used like this:

```bash
yarn manypkg run pkg-a start
yarn manypkg run a start
yarn manypkg run package-a start
yarn manypkg run @project/package-a start
yarn manypkg run packages/pkg-a start
yarn manypkg run package-b start
yarn manypkg run b start
```

See more details at https://github.com/thinkmill/manypkg
The following wouldn't work though because the `package` and `pkg` aren't unique among the package names/directories:

```bash
yarn manypkg run package start
yarn manypkg run pkg start
```
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"validate-npm-package-name": "^3.0.0"
},
"devDependencies": {
"fixturez": "^1.1.0"
"fixturez": "^1.1.0",
"strip-ansi": "^6.0.0"
},
"preconstruct": {
"entrypoints": [
Expand Down
84 changes: 84 additions & 0 deletions packages/cli/src/__snapshots__/run.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Run command should execute "package start" and exit with 1: stderr 1`] = `
"Browserslist: caniuse-lite is outdated. Please run next command \`yarn upgrade\`
☔️ error an identifier must only match a single package but \\"package\\" matches the following packages:
☔️ error @manypkg/basic-fixture-pkg-one
☔️ error @manypkg/basic-fixture-pkg-two
"
`;

exports[`Run command should execute "package start" and exit with 1: stdout 1`] = `""`;

exports[`Run command should execute "package-one start" and exit with 0: stderr 1`] = `
"Browserslist: caniuse-lite is outdated. Please run next command \`yarn upgrade\`
warning package.json: No license field
warning ../../package.json: No license field
"
`;

exports[`Run command should execute "package-one start" and exit with 0: stdout 1`] = `
"$ echo hello start
hello start
"
`;

exports[`Run command should execute "package-one test" and exit with 0: stderr 1`] = `
"Browserslist: caniuse-lite is outdated. Please run next command \`yarn upgrade\`
warning package.json: No license field
warning ../../package.json: No license field
"
`;

exports[`Run command should execute "package-one test" and exit with 0: stdout 1`] = `
"$ echo testing
testing
"
`;

exports[`Run command should execute "package-three start" and exit with 1: stderr 1`] = `
"Browserslist: caniuse-lite is outdated. Please run next command \`yarn upgrade\`
☔️ error No matching packages found
"
`;

exports[`Run command should execute "package-three start" and exit with 1: stdout 1`] = `""`;

exports[`Run command should execute "package-two something" and exit with 1: stderr 1`] = `
"Browserslist: caniuse-lite is outdated. Please run next command \`yarn upgrade\`
warning package.json: No license field
warning ../../package.json: No license field
error Command \\"something\\" not found.
"
`;

exports[`Run command should execute "package-two something" and exit with 1: stdout 1`] = `
"info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
"
`;

exports[`Run command should execute "package-two start" and exit with 0: stderr 1`] = `
"Browserslist: caniuse-lite is outdated. Please run next command \`yarn upgrade\`
warning package.json: No license field
warning ../../package.json: No license field
"
`;

exports[`Run command should execute "package-two start" and exit with 0: stdout 1`] = `
"$ echo hello start
hello start
"
`;

exports[`Run command should execute "pkg-one start" and exit with 0: stderr 1`] = `
"Browserslist: caniuse-lite is outdated. Please run next command \`yarn upgrade\`
warning package.json: No license field
warning ../../package.json: No license field
"
`;

exports[`Run command should execute "pkg-one start" and exit with 0: stdout 1`] = `
"$ echo hello start
hello start
"
`;
6 changes: 5 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Workspace, Check } from "./checks/utils";
import { checks } from "./checks";
import { ExitError } from "./errors";
import { writeWorkspace } from "./utils";
import { runCmd } from "./run";
import spawn from "spawndamnit";
import pLimit from "p-limit";

Expand Down Expand Up @@ -86,9 +87,12 @@ async function execCmd(args: string[]) {
if (things[0] === "exec") {
return execCmd(things.slice(1));
}
if (things[0] === "run") {
return runCmd(things.slice(1), process.cwd());
}
if (things[0] !== "check" && things[0] !== "fix") {
logger.error(
`command ${things[0]} not found, only check, exec and fix exist`
`command ${things[0]} not found, only check, exec, run and fix exist`
);
throw new ExitError(1);
}
Expand Down
34 changes: 34 additions & 0 deletions packages/cli/src/run.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { runCmd } from "./run";
import fixturez from "fixturez";
import spawn from "spawndamnit";
import stripAnsi from "strip-ansi";

const f = fixturez(__dirname);

describe("Run command", () => {
it.each([
["package-one", "start", 0],
["package-one", "test", 0],
["package-two", "start", 0],
["package", "start", 1],
["package-two", "something", 1],
["package-three", "start", 1],
["pkg-one", "start", 0]
// @ts-ignore
])(
'should execute "%s %s" and exit with %i',
async (arg0, arg1, exitCode) => {
const { stdout, stderr, code } = await spawn(
require.resolve("../bin"),
// @ts-ignore
["run", arg0, arg1],
{
cwd: f.find("basic-with-scripts")
}
);
expect(code).toBe(exitCode);
expect(stripAnsi(stdout.toString())).toMatchSnapshot("stdout");
expect(stripAnsi(stderr.toString())).toMatchSnapshot("stderr");
}
);
});
41 changes: 41 additions & 0 deletions packages/cli/src/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import findWorkspacesRoot from "find-workspaces-root";
import getWorkspaces from "get-workspaces";
import path from "path";
import spawn from "spawndamnit";
import * as logger from "./logger";
import { ExitError } from "./errors";

export async function runCmd(args: string[], cwd: string) {
let workspacesRoot = await findWorkspacesRoot(cwd);
let workspaces = (await getWorkspaces({
cwd: workspacesRoot,
tools: ["yarn", "bolt", "pnpm", "root"]
}))!;

const matchingWorkspaces = workspaces.filter(workspace => {
return (
workspace.name.includes(args[0]) ||
path.relative(workspacesRoot, workspace.dir).includes(args[0])
);
});

if (matchingWorkspaces.length > 1) {
logger.error(
`an identifier must only match a single package but "${
args[0]
}" matches the following packages: \n${matchingWorkspaces
.map(x => x.name)
.join("\n")}`
);
throw new ExitError(1);
} else if (matchingWorkspaces.length === 0) {
logger.error("No matching packages found");
throw new ExitError(1);
} else {
const { code } = await spawn("yarn", args.slice(1), {
cwd: matchingWorkspaces[0].dir,
stdio: "inherit"
});
throw new ExitError(code);
}
}
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2152,6 +2152,11 @@ ansi-regex@^4.0.0, ansi-regex@^4.1.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==

ansi-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==

ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
Expand Down Expand Up @@ -12786,6 +12791,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"

strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
dependencies:
ansi-regex "^5.0.0"

strip-bom-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92"
Expand Down

0 comments on commit 63cdae1

Please sign in to comment.