twitter/bower — pull request #326 by Coridyn (2013-03-17 06:10:11+00:00)
Title: Install events

I've added the 'package' and 'install' events to be emitted from the install command as part of [#233](https://github.com/twitter/bower/issues/233).

* The 'package' event is emitted once for each package when it has been installed and passes information about the package that has been installed.

* The 'install' event is emitted once all packages have been installed but before the 'end' event. The event passes a map of all the packages that have been installed (with a similar structure to the `Manager.dependencies` property).

Notes on the implementation:

1. The events pass anonymous objects with a subset of the Package information to avoid exposing the Package, Manager and UnitWork types directly from these events. The data object is returned by the `Package.getPublicPackage()` method internally and includes these properties: name, localPath, json, version, tag, dependencies, installed. The 'dependencies' property is also filtered to exclude internal types. See item 3 below about the 'installed' property.

2. The `Package.getPublicPackage()` method returns the same object instance each time it is called (but copies over the current Package information each time). My reasoning is that it would be useful for people processing the event data to be able to compare the package objects directly with === rather than having to check the package name, version and possibly other information to determine equality. If this method returned a new object instance each time then it would be much more difficult to test package equality.

3. I've added an `installed` property to the Package type. This is to differentiate between project dependency packages that have only been resolved and those that have been resolved and installed. For example the 'test/assets/project-install-conflict' project resolves three different jquery dependency packages (1.8.3, 1.6.0 and another one without a version). The `installed` property is only true for the jquery package that 'wins' the dependency resolution competition (in this case, jquery 1.8.3) - this lets event listeners work out which packages have been installed to disk and which haven't.

4. The callback signatures for the 'package' and 'install' events are as follows:

```javascript
install()
.on('package', function(pkg){
// An example `pkg` object:
{
'name': 'project',
'localPath': '/local/path/to/project/components/project',
'json': {
'name': 'package-deps-on-jquery',
'version': '1.0.0',
'dependencies': {
'jquery': 'test/assets/package-jquery-copy'
},
'_id': 'package-deps-on-jquery@1.0.0',
'readme': 'ERROR: No README.md file found!',
'description': 'ERROR: No README.md file found!'
},
'version': '1.0.0',
'tag': '1.0.0',
'installed': false,
'dependencies': {
// ... see below for this structure.
}
}

// The `dependencies` property follows the same structure as `Manager.dependencies`
// and maps dependency names to a list of resolved dependency packages.
// This means that there may be multiple dependencies listed under the same name however
// at most one of them should have `installed === true` (as determined by Bower's
// dependency resolution process).
//
// An example dependency listing for multiple versions of jquery might look like this:
'dependencies': {

'jquery': [{
'name': 'jquery',
'localPath': '/local/path/to/project/components/jquery',
'json': {
'name': 'jquery',
'version': '1.8.1',
'main': [
'index.js'
],
'dependencies': {},
'_id': 'jquery@1.8.1',
'readme': 'ERROR: No README.md file found!',
'description': 'ERROR: No README.md file found!'
},
'version': '1.8.1',
'tag': undefined,
'installed': true,
'dependencies': {}
},
{
'name': 'jquery',
'localPath': '/local/path/to/project/components/jquery',
'json': {
'name': 'jquery',
'version': '1.6.0',
'main': [
'index.js'
],
'dependencies': {},
'_id': 'jquery@1.6.0',
'readme': 'ERROR: No README.md file found!',
'description': 'ERROR: No README.md file found!'
},
'version': '1.6.0',
'tag': undefined,
'installed': false,
'dependencies': {}
},
// Additional dependency packages may be listed here.
]

}

})
.on('install', function(packages){
// `packages` maps package names to an array of packages with that package name.
// It follows the same structure as the 'dependencies' example given above.
{
'jquery': [
{
// A package object as defined in the 'package' event above.
}
],
'project': [
// Additional package objects may be listed here.
],
// Additional package mappings may be listed here.
}

})
```