# grunt-thrall

> Grunt task orchestrator/warchief

Latest version **0.0.4** (published 2015-05-29) · MIT license · 0 weekly downloads

## Install

```sh
npm install grunt-thrall
pnpm add grunt-thrall
yarn add grunt-thrall
bun add grunt-thrall
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.0.4 |
| Published | 2015-05-29 |
| First published | 2015-05-26 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 10 |
| Known vulnerabilities | 0 (+12 in 3 direct dependencies) |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Hannes Diercks |
| Maintainers | xiphe |
| Keywords | grunt, collection, orchestration, tasks |

## Links

- npm: https://www.npmjs.com/package/grunt-thrall
- Repository: git@github.com:Xiphe/Thrall
- Homepage: https://github.com/Xiphe/Thrall
- Issues: https://github.com/Xiphe/Thrall/issues
- npm.io page: https://npm.io/package/grunt-thrall

## Dependencies (10)

- [di](https://npm.io/package/di.md) 0.0.1
- [del](https://npm.io/package/del.md) ^1.2.0
- [npm](https://npm.io/package/npm.md) ^2.9.1
- [glob](https://npm.io/package/glob.md) ^5.0.6
- [lodash](https://npm.io/package/lodash.md) ^3.8.0
- [mkdirp](https://npm.io/package/mkdirp.md) ^0.5.1
- [install](https://npm.io/package/install.md) ^0.1.8
- [minimist](https://npm.io/package/minimist.md) ^1.1.1
- [getobject](https://npm.io/package/getobject.md) ^0.1.0
- [findup-sync](https://npm.io/package/findup-sync.md) ^0.2.1

## Alternatives

- [jsforce](https://npm.io/package/jsforce.md) — 851.2K weekly downloads
- [react-native-qrcode-svg](https://npm.io/package/react-native-qrcode-svg.md) — 693.5K weekly downloads
- [@salesforce/plugin-data](https://npm.io/package/@salesforce/plugin-data.md) — 394.9K weekly downloads
- [@backstage/plugin-search-common](https://npm.io/package/@backstage/plugin-search-common.md) — 308.5K weekly downloads
- [@chain-registry/types](https://npm.io/package/@chain-registry/types.md) — 38.4K weekly downloads

## Recent versions

- 0.0.4 (latest) — 2015-05-29
- 0.0.3 — 2015-05-29
- 0.0.2 — 2015-05-27
- 0.0.1 — 2015-05-26

## README

grunt-thrall
============

[![Build Status](https://travis-ci.org/Xiphe/Thrall.svg?branch=master)](https://travis-ci.org/Xiphe/Thrall)
[![Code Climate](https://codeclimate.com/github/Xiphe/Thrall/badges/gpa.svg)](https://codeclimate.com/github/Xiphe/Thrall)
[![Test Coverage](https://codeclimate.com/github/Xiphe/Thrall/badges/coverage.svg)](https://codeclimate.com/github/Xiphe/Thrall/coverage)
[![Dependency Status](https://david-dm.org/Xiphe/Thrall.svg)](https://david-dm.org/Xiphe/Thrall)

Grunt task orchestrator/warchief

> "The beginning of wisdom is the statement 'I do not know.' The person who cannot make that statement is one who will never learn anything. And I have prided myself on my ability to learn." - Thrall in Cycle of Hatred, page 77


Why?
----

When a project uses lots of Grunt Tasks, the `Gruntfile.js` tends to get really messy and
hard to maintain. 

With [grunt-angular-toolbox](https://github.com/Jimdo/grunt-angular-toolbox), we tried to extract this 
complexity into a toolbox that handles all grunt related things for a main project.

This works fine, but the toolbox itself was still spaghetti-code-ish and hard to understand
and maintain for most users. 

Thrall contains all orchestration logic and is 100% test covered. This allows consuming packages
to focus on task definition without having to worry to much about configuration loading and
option handling.


Usage
----- 

Install the module:

`npm install grunt-thrall --save-dev`


```js
// gruntfile.js
module.exports = function(grunt) {
	var thrall = require('grunt-thrall');
	
	thrall.init({
		/* see config */	
	});	
};
```

This can be used for any project or grunt plugin. See:

 - [Example project](https://github.com/Xiphe/Thrall/tree/master/sample_project)
 - [Grunt Plugin](https://github.com/Jimdo/grunt-angular-toolbox)


API
---

### `thrall.init(config)`

 - Load all grunt plugins, specified in the projects `package.json`
 (heavily inspired by [load-grunt-tasks](https://github.com/sindresorhus/load-grunt-tasks))
 - merge defaults, provided in `config` with settings is `grunt.config`
 - Search for custom tasks, specified in the `tasks/` directory
 - Dynamically load configuration for grunt plugins used by those tasks from `config/` directory


Config
------

### Required _string_: `dir`

```js 
thrall.init({dir: __dirname + 'myTasks' /* ,... */ });
```

The basic directory for custom tasks and grunt plugin configuration.

Expects subdir `tasks/` for custom tasks and `config/` for grunt plugin configuration
to be present.


### Required _string_: `basePath`

```js 
thrall.init({basePath: __dirname /* ,... */ });
```

The projects base path.

Used to findup `node_modules/grunt-*/tasks/*` when auto-loading grunt plugins.


### Required _object_: `grunt`

```js 
thrall.init({grunt: grunt /* ,... */ });
```

The currently running grunt instance.


### _string_: `name`

```js 
thrall.init({name: 'myProject' /* ,... */ });
```

Defaults to `config.pkg.name` project name from `package.json`

This is also the key for custom configuration that is merged with the defaults

```js
// pseudo-code
var config = _.merge(config.getDefaults(), grunt.config(config.name));
```

### _boolean_: `loadDevDependencies`

```js 
thrall.init({loadDevDependencies: false /* ,... */ });
```

Default: `true`

Whether or not to include `devDependencies` from `package.json` when auto-loading grunt plugins.


### _boolean_: `loadDependencies`

```js 
thrall.init({loadDependencies: true /* ,... */ });
```

Default: `false`

Whether or not to include `dependencies` from `package.json` when auto-loading grunt plugins.


### _object_: `module`

```js 
thrall.init({
	module: {
		myHelper: ['factory', require('./helpers/myHelper')]
	}
	/* ,... */ 
});
```

Default: `{}`

Task definitions, grunt plugin configurations and getDefaults are being invoked using 
[node-di](https://github.com/vojtajina/node-di) providing basic node modules.

When you need a custom helper, it can be registered here.

See [DI](#DI) for further informations.


### _function_: `getDefaults`

```js 
thrall.init({
	getDefaults: function(/* di here */) {
		return {
			foo: 'bar'
		}
	}
	/* ,... */ 
});
```

Default configuration will be merged and be available as `grunt.config(config.name)`.


Task Factories
--------------

every file in `config.dir`/`tasks/` is expected to export a factory function, returning 
a task definition object. The name will be generated by the path relative to the tasks dir.

Factories are being invoked using node-di, see [DI](#DI) for further informations.

### Naming

```js
// tasks/foo/bar.js
module.exports = function(/* di here */) {
	return {};
};
```

will register task `foo:bar` that, when can be called with `grunt foo:bar` and does nothing.

### _string/array_: `description`

```js
module.exports = function() {
	return {
		description: [
			'this is the bar tasks',
			'it will foo.'
		]
		/* ... */
	};
};
```

Task description, which is displayed by `grunt --help`.
Arrays will be `.join('\n')`-ed.


### _array_: `run`

Subtasks to run by this task.

```js
// tasks/foo/bar.js
module.exports = function() {
	return {
		/* ... */
		run: [
			'jshint:src',
			'mochaTest'
		]
	};
};
```

Will load for grunt plugin configurations from
`config/jshint/src.js` and `config/mochaTest.js` (see 
[Configuration Factories](#configuration-factories))
and execute both subtasks when `grunt foo:bar` is called.


#### runIf blocks

A runIf block can add tasks to the cue based on grunt configuration.

```js
module.exports = function() {
	return {
		/* ... */
		run: [
			'other:task',
			{
				if: 'coverage.enabled',
				task: ['coverage']
			},
			{
				if: [
					(null != 1),
					'foo.bar'
				],
				task: 'report',
				else: 'say:goodbye'
			}
		]
	};
};
```

In the above example:

 - the `coverage` task will only run when `grunt.config.get('coverage.enabled')` returns a truthy value.
 - the `report` task will run when `grunt.config.get('foo.bar')` is truthy, 
   (and `null != 1` which is of cause true, too)
 - when `grunt.config.get('foo.bar')` is falsy the `say:goodbye` task runs instead

All expressions and config values in an `if`-array need to be true in order to run the task.
There is no `OR` operator or `!`-negation.

This works well with [options](#object-options).


### _object_: `options`

Map CLI options, environment variables and grunt modifiers to grunt config.

```js
// tasks/foo/bar.js
module.exports = function() {
	return {
		/* ... */
		options: {
			coverage: 'coverage.enabled'
		}
	};
};
```

`grunt foo:bar --coverage` will set the `grunt.config('coverage.enabled')` to true.

```js
// tasks/foo/bar.js
module.exports = function() {
	return {
		/* ... */
		options: {
			'demo-port': {
				env: 'DEMO_PORT',
				alias: 'port',
				key: 'foo.demoPort'
			}
		}
	};
};
```
either of 

 - `grunt foo:bar --demo-port=7000`
 - `grunt foo:bar --demo=7000`
 - `DEMO_PORT=7000 grunt foo:bar`

will set the `grunt.config('foo.demoPort')` to 7000.

```js
// tasks/foo/bar.js
module.exports = function() {
	return {
		/* ... */
		options: {
			coverage: {
				grunt: ':coverage',
				key: 'coverage.enabled'
			}
		}
	};
};
```

`grunt foo:bar:coverage` will set `grunt.config('coverage.enabled')` to true.


### _function_: `runFilter`

Filter that may manipulate the tasks cue before execution.

```js
// tasks/foo/bar.js
module.exports = function() {
	return {
		/* ... */
		run: ['foo', 'bar'],
		runFilter: function(tasks, args) {
			if (args[0] === 'baz') {
				tasks.shift();
			}
			return tasks;
		}
	};
};
```

In this case, when `grunt foo:bar:baz` is called, only the `foo` subtask will run.


Configuration Factories
-----------------------

every file in `config.dir`/`config/` is expected to export a factory function, returning 
a configuration object. The name has to match the path that this configuration will 
be placed at, in the grunt config.

Factories are being invoked using node-di, see [DI](#DI) for further informations.

```js
// config/jshint/src.js
module.exports = function(/* di here */) {
	return {
		options: {
			ignores: ['**/*.coffee'],
			jshintrc: true,
		},
		src: [
			'<%= my.src.files.js %>'
		]
	};
};
```

This is similar to the following standard configuration, only that it's
split in to a lot of small files, with is more easy to maintain for big projects. 

```js
grunt.initConfig({
	jshint: {
		src: {
			options: {
				ignores: ['**/*.coffee'],
				jshintrc: true,
			},
			src: [
				'<%= my.src.files.js %>'
			]
		}
	}
});
```


DI
--

[`getDefaults`](#function-getdefaults), [Task Factories](#task-factories) and 
[Configuration Factories](#task-factories) are being invoked with a 
[node-di](https://github.com/vojtajina/node-di) module, providing the following
services:

 - `_`: [lodash](https://github.com/lodash/lodash)
 - `cliOptions`: CLI Options using [minimist](https://github.com/substack/minimist)
 - `del`: [del](https://github.com/sindresorhus/del)
 - `findupSync`: [node-findup-sync](https://github.com/cowboy/node-findup-sync)
 - `fs`: node module
 - `getobject`: [node-getobject](https://github.com/cowboy/node-getobject)
 - `glob`: [node-glob](https://github.com/isaacs/node-glob)
 - `grunt`: currently running grunt instance
 - `mkdirp`: [node-mkdirp](https://github.com/substack/node-mkdirp) 
 - `path`: node module
 - `merged`: (`getDefaults` ONLY) See [merged callback](#merged-callback)
 - `name`: (Task Factories ONLY) name of the task
 - `rootTask`: (Task Factories ONLY) the name of the task that has actually been called


#### merged callback

```js
/* ... */
getDefaults: function(merged) {
	merged(function(mergedConfig) {
		mergedConfig.foo = 'baz';
	});
	return {foo: 'bar'};
}
```


## [MIT License](https://raw.github.com/Xiphe/thrall/master/LICENSE)

---
_Source: https://npm.io/package/grunt-thrall · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
