for-each-project
================

[![for-each-project npm package version](https://img.shields.io/npm/v/for-each-project.svg)](https://www.npmjs.com/package/for-each-project)
[![for-each-project License: GPLv3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl.txt)
[![for-each-project monthly downloads](https://img.shields.io/npm/dm/for-each-project.svg)](https://www.npmjs.com/package/for-each-project)
[![Follow this project on Twitter](https://img.shields.io/twitter/follow/buildverse.svg?style=social&label=Follow)](https://twitter.com/buildverse)

> Processes a set of project directories defined in a package.json file

* [About](#about)
* [Installing](#installing)
* [Using](#using)
* [Command-line Usage Docs](#command-line-usage-docs)
* [Support](#support)
* [License](#license)

About
-----

**for-each-project** is one of several utility packages in the
[buildverse tool suite](https://www.npmjs.com/package/buildverse).  It is a
command-line utility that is intended to be <ins>invoked as part of an npm
package script</ins> to process a set of project directories defined in the
enclosing `package.json` file.

To see how some projects are using *for-each-project*, peruse the `package.json`
files the following projects. *Note that these projects will generally contain
several `package.json` files spread across different folders within the project.*

  * [buildverse-sdk](https://github.com/Typeverse/buildverse-sdk)
  * [cowarray-develop](https://github.com/indiescripter/cowarray-develop)

Like the concept of this project? Let other developers know with a
[tweet](https://t.co/jvGV2TXgT3).

Installing
----------

*for-each-project* requires

  * [node](https://nodejs.org/) version 4.8.4+

and, at your option, any of these package managers:

  * [npm](https://www.npmjs.com/package/npm) version 5.3.0+
  * [pnpm](https://www.npmjs.com/package/pnpm) version 1.16.0+
  * [yarn](https://www.npmjs.com/package/yarn) version 1.1.0+

This package is automatically installed alongside all other packages in the
[buildverse tool suite](https://www.npmjs.com/package/buildverse) with the
single install command

> `npm install --save-dev buildverse`

though <ins>it can be installed and used separately on its own</ins> by

> `npm install --save-dev for-each-project`

if you wish to use it standalone.

*Note the use of `--save-dev` since buildverse tool suite packages are typically
used during development as opposed to being deployed in production, runtime
environment.*

Using
-----

The following demonstration is for a Linux terminal session.  Use on Windows
systems is the same, albeit modulo Windows command-line idiosyncrasies such as
the use of backslashes ('\\') versus foreslashes ('/') for path separators, etc.

**Commands**

```sh
# Create a directory as a top-level development project,
# change into that directory and initialize a
# package.json file.
$ mkdir foobar-project
$ cd foobar-project
$ npm init -y

# Choose only one of the following install commands:
$ npm install --save-dev buildverse
# or
$ npm install --save-dev for-each-project

# Create a subdirectory as a subproject/package, change
# into that directory and initialize a package.json
# file.
$ mkdir foo
$ cd foo
$ npm init -y
$ cd ..

# For the purpose of this example, repeat for a second
# subproject/package.
$ mkdir bar
$ cd bar
$ npm init -y
$ cd ..
```

**Result**

At this point the `foobar-project` directory layout is:

```
foobar-project/
  bar/
    package.json
  foo/
    package.json
  node_modules/
    ...
  package.json
  package-lock.json
```

**Configure package.json to use for-each-project**

Now edit `foobar-project/package.json` making these changes:

  1. add a `private` property with the value `true`,
  2. change the `test` script to `for-each-project apply-cmdline`, and
  3. add a `buildverse` section containing an array of subproject
  names (`["foo", "bar"]`) under the key, `subprojects`.

The result should look something like this (irrelevant fields omitted):

*foobar-project/package.json:*
```
{
  "private": true,
  "name": "foobar-project",
  "version": "1.0.0",
  "scripts": {
    "test": "for-each-project apply-cmdline"
  },
  "devDependencies": {
    "for-each-project": "^0.2.20"
  },
  "buildverse": {
    "subprojects": [
      "foo",
      "bar"
    ]
  }
}
```

**Commands**

From the command line now run the npm `test` script (while still in the
`foobar-project` directory).

```sh
$ npm test
``` 

**Output**

```
> foobar-project@0.1.0 test /home/.../tmp/foobar-project
> for-each-project apply-cmdline

(cd foo && /usr/lib/node_modules/npm/bin/npm-cli.js test)

> foo@0.1.0 test /home/.../tmp/foobar-project/foo
> echo "Error: no test specified" && exit 1

Error: no test specified
npm ERR! Test failed.  See above for more details.
npm ERR! Test failed.  See above for more details.
```

Notice what happened.  The effect of `for-each-project apply-cmdline` was to
cause the shell to change into the `foo` subdirectory and execute the `test`
script contained in the `package.json` file from that directory.  At that point
the `npm test` command abruptly ended with `Test failed` errors.  This is
because *foo*'s `test` script shell command exited with code 1 which indicates
failure:

```
echo \"Error: no test specified\" && exit 1`
```

Let's change that outcome to achieve a successful run.  Edit the `test` script
in both `foo/package.json` and `bar/package.json` to now read:

*foo/package.json:*
```
  "scripts": {
    "test": "echo testing-foo-subproject"
  }
```

*bar/package.json:*
```
  "scripts": {
    "test": "echo testing-bar-subproject"
  }
```

**Commands**

The effect now of running `npm test` from (the parent) directory
`foobar-project` is that both subproject (child) directories `foo` and `bar` are
visted and their respective `test` scripts are executed (since both their
associated shell commands exited with code 0 which indicates success).

```sh
$ npm test
``` 

**Output**

```
> foobar-project@0.1.0 test /home/.../tmp/foobar-project
> for-each-project apply-cmdline

(cd foo && /usr/lib/node_modules/npm/bin/npm-cli.js test)

> foo@0.1.0 test /home/.../tmp/foobar-project/foo
> echo testing-foo-subproject

testing-foo-subproject
(cd bar && /usr/lib/node_modules/npm/bin/npm-cli.js test)

> bar@0.1.0 test /home/.../tmp/foobar-project/bar
> echo testing-bar-subproject

testing-bar-subproject
```

**Summary**

Summing up, when used in an npm package script `for-each-project apply-cmdline`
does the following things:

1. Consults the `package.json` file in the current working directory for a
special configuration object under the key `buildverse`.  This object contains
an array, `subprojects`, of directory pathnames representing (sub)projects.  The
pathnames are relative to the location of the containing `package.json` file.
2. Each directory entry in this array is then processed by:

  * Changing the current working directory to the given directory
  * Recursively executing the package manager program (npm, pnpm or yarn) with
    the same command line arguments that it was originally executed with.

If `for-each-project` is invoked with the `--reverse` switch, the directory set
is processed in reverse order.

This is the bare bones of information you need to start using *for-each-project*
in your own npm package scripts. See the Command-line Usage Docs section for
detailed information about the `for-each-project` command. 

Command-line Usage Docs
-----------------------

* [for-each-project command](https://github.com/Typeverse/buildverse-sdk/blob/master/docs/for-each-project.md)

Support
-------

If you run into any problems or have constructive criticism, suggestions for
improvement or just plain encouragement to offer please
[open or comment on an issue](https://github.com/Typeverse/buildverse-sdk/issues)
on GitHub.

**Note that issues are managed under the
[buildverse-sdk](https://github.com/Typeverse/buildverse-sdk) umbrella project.
**

You can also show your support for this project by following 
[BuildverseJS, @buildverse on Twitter](https://twitter.com/buildverse) or simply
by letting other developers know with a [tweet](https://t.co/jvGV2TXgT3).

License
-------

Currently this project is licensed
[GPLv3](https://github.com/Typeverse/buildverse-sdk/blob/master/for-each-project/LICENSE-GPL-3.0.txt).

The author is working however towards gaining community-funding so as to make
this a successful and sustainable, quality open-source project with an
**MIT license**.

Copyright © 2017 Justin Johansson
([https://github.com/indiescripter](https://github.com/indiescripter)).
