Comparing version 0.4.2 to 0.4.3
444
lib/Dgeni.js
@@ -0,8 +1,11 @@ | ||
"use strict"; | ||
/* tslint globals: require: true */ | ||
var _ = require('lodash'); | ||
var di = require('di'); | ||
var Package = require('./Package'); | ||
var sortByDependency = require('./util/dependency-sort'); | ||
var validate = require('validate.js'); | ||
var Q = require('q'); | ||
var Package_1 = require("./Package"); | ||
var processorValidation_1 = require("./legacyPackages/processorValidation"); | ||
var dependency_sort_1 = require("./util/dependency-sort"); | ||
var getInjectables_1 = require("./util/getInjectables"); | ||
var log_1 = require("./util/log"); | ||
/** | ||
@@ -13,220 +16,227 @@ * Create an instance of the Dgeni documentation generator, loading any packages passed in as a | ||
*/ | ||
function Dgeni(packages) { | ||
this.packages = {}; | ||
packages = packages || []; | ||
if ( !Array.isArray(packages) ) { throw new Error('packages must be an array'); } | ||
_.map(packages, this.package, this); | ||
} | ||
/** | ||
* @type {Package} | ||
*/ | ||
Dgeni.Package = Package; | ||
/** | ||
* Load a package into dgeni | ||
* @param {Package|string} package The package to load or the name of a new package to | ||
* create. | ||
* @param {Array.<Package|string>} dependencies A collection of dependencies for this package | ||
* @return {Package} The package that was loaded, to allow chaining | ||
*/ | ||
Dgeni.prototype.package = function(package, dependencies) { | ||
if ( typeof package === 'string' ) { package = new Package(package, dependencies); } | ||
if ( !(Package.isPackage(package)) ) { throw new Error('package must be an instance of Package'); } | ||
if ( this.packages[package.name] ) { | ||
throw new Error('The "' + package.name + '" package has already been loaded'); | ||
} | ||
this.packages[package.name] = package; | ||
// Extract all inline packages and load them into dgeni; | ||
package.namedDependencies = package.dependencies.map(function(dependency) { | ||
if ( Package.isPackage(dependency) ) { | ||
// Only load dependent package if not already loaded | ||
if ( !this.packages[dependency.name] ) { this.package(dependency); } | ||
return dependency.name; | ||
var Dgeni = (function () { | ||
function Dgeni(packages) { | ||
if (packages === void 0) { packages = []; } | ||
var _this = this; | ||
this.packages = {}; | ||
if (!Array.isArray(packages)) { | ||
throw new Error('packages must be an array'); | ||
} | ||
// Add in the legacy validation that was originally part of the core Dgeni tool. | ||
this.package(processorValidation_1.processorValidationPackage); | ||
packages.map(function (p) { return _this.package(p); }); | ||
} | ||
return dependency; | ||
}, this); | ||
// Return the package to allow chaining | ||
return package; | ||
}; | ||
/** | ||
* Configure the injector using the loaded packages. | ||
* | ||
* The injector is assigned to the `injector` property on `this`, which is used by the | ||
* `generate()` method. Subsequent calls to this method will just return the same injector. | ||
* | ||
* This method is useful in unit testing services and processors as it gives an easy way to | ||
* get hold of an instance of a ready instantiated component without having to load in all | ||
* the potential dependencies manually: | ||
* | ||
* ``` | ||
* var Dgeni = require('dgeni'); | ||
* | ||
* function getInjector() { | ||
* var dgeni = new Dgeni(); | ||
* dgeni.package('testPackage', [require('dgeni-packages/base')]) | ||
* .factory('templateEngine', function dummyTemplateEngine() {}); | ||
* return dgeni.configureInjector(); | ||
* }; | ||
* | ||
* describe('someService', function() { | ||
* var someService; | ||
* beforeEach(function() { | ||
* var injector = getInjector(); | ||
* someService = injector.get('someService'); | ||
* }); | ||
* | ||
* it("should do something", function() { | ||
* someService.doSomething(); | ||
* ... | ||
* }); | ||
* }); | ||
* ``` | ||
*/ | ||
Dgeni.prototype.configureInjector = function() { | ||
if ( !this.injector ) { | ||
// Sort the packages by their dependency - ensures that services and configs are loaded in the | ||
// correct order | ||
var packages = sortByDependency(this.packages, 'namedDependencies'); | ||
// Create a module containing basic shared services | ||
var dgeniConfig = { | ||
stopOnValidationError: true, | ||
stopOnProcessingError: true | ||
/** | ||
* Load a package into dgeni | ||
* @param package The package to load or the name of a new package to create. | ||
* @param dependencies A collection of dependencies for this package | ||
* @return The package that was loaded, to allow chaining | ||
*/ | ||
Dgeni.prototype.package = function (pkg, dependencies) { | ||
var _this = this; | ||
if (dependencies === void 0) { dependencies = []; } | ||
if (this.injector) { | ||
throw new Error('injector already configured - you cannot add a new package'); | ||
} | ||
if (typeof pkg === 'string') { | ||
pkg = new Package_1.Package(pkg, dependencies); | ||
} | ||
if (!(Package_1.Package.isPackage(pkg))) { | ||
throw new Error('package must be an instance of Package'); | ||
} | ||
if (this.packages[pkg.name]) { | ||
throw new Error('The "' + pkg.name + '" package has already been loaded'); | ||
} | ||
this.packages[pkg.name] = pkg; | ||
// Extract all inline packages and load them into dgeni; | ||
pkg.namedDependencies = pkg.dependencies.map(function (dependency) { | ||
if (Package_1.Package.isPackage(dependency)) { | ||
// Only load dependent package if not already loaded | ||
if (!_this.packages[dependency.name]) { | ||
_this.package(dependency); | ||
} | ||
return dependency.name; | ||
} | ||
return dependency; | ||
}); | ||
// Return the package to allow chaining | ||
return pkg; | ||
}; | ||
var dgeniModule = new di.Module() | ||
.value('dgeni', dgeniConfig) | ||
.factory('log', require('./util/log')) | ||
.factory('getInjectables', require('./util/getInjectables')); | ||
// Create the dependency injection container, from all the packages' modules | ||
var modules = packages.map(function(package) { return package.module; }); | ||
modules.unshift(dgeniModule); | ||
// Create the injector and | ||
var injector = this.injector = new di.Injector(modules); | ||
// Apply the config blocks | ||
packages.forEach(function(package) { | ||
package.configFns.forEach(function(configFn) { | ||
injector.invoke(configFn); | ||
}); | ||
}); | ||
// Get the collection of processors | ||
// We use a Map here so that we only get one of each processor name | ||
var processors = this.processors = {}; | ||
packages.forEach(function(package) { | ||
package.processors.forEach(function(processorName) { | ||
var processor = injector.get(processorName); | ||
processor.name = processorName; | ||
processor.$package = package.name; | ||
// Ignore disabled processors | ||
if ( processor.$enabled !== false ) { | ||
processors[processorName] = processor; | ||
/** | ||
* Configure the injector using the loaded packages. | ||
* | ||
* The injector is assigned to the `injector` property on `this`, which is used by the | ||
* `generate()` method. Subsequent calls to this method will just return the same injector. | ||
* | ||
* This method is useful in unit testing services and processors as it gives an easy way to | ||
* get hold of an instance of a ready instantiated component without having to load in all | ||
* the potential dependencies manually: | ||
* | ||
* ``` | ||
* const Dgeni = require('dgeni'); | ||
* | ||
* function getInjector() { | ||
* const dgeni = new Dgeni(); | ||
* dgeni.package('testPackage', [require('dgeni-packages/base')]) | ||
* .factory('templateEngine', function dummyTemplateEngine() {}); | ||
* return dgeni.configureInjector(); | ||
* }; | ||
* | ||
* describe('someService', function() { | ||
* const someService; | ||
* beforeEach(function() { | ||
* const injector = getInjector(); | ||
* someService = injector.get('someService'); | ||
* }); | ||
* | ||
* it("should do something", function() { | ||
* someService.doSomething(); | ||
* ... | ||
* }); | ||
* }); | ||
* ``` | ||
*/ | ||
Dgeni.prototype.configureInjector = function () { | ||
var _this = this; | ||
if (!this.injector) { | ||
// Sort the packages by their dependency - ensures that services and configs are loaded in the | ||
// correct order | ||
var packages = this.packages = dependency_sort_1.sortByDependency(this.packages, 'namedDependencies'); | ||
// Create a module containing basic shared services | ||
this.stopOnProcessingError = true; | ||
var dgeniModule = new di.Module() | ||
.value('dgeni', this) | ||
.factory('log', log_1.logFactory) | ||
.factory('getInjectables', getInjectables_1.getInjectablesFactory); | ||
// Create the dependency injection container, from all the packages' modules | ||
var modules = packages.map(function (pkg) { return pkg.module; }); | ||
modules.unshift(dgeniModule); | ||
// Create the injector and | ||
var injector_1 = this.injector = new di.Injector(modules); | ||
// Apply the config blocks | ||
packages.forEach(function (pkg) { return pkg.configFns.forEach(function (configFn) { return injector_1.invoke(configFn); }); }); | ||
// Get the the processors and event handlers | ||
var processorMap_1 = {}; | ||
this.handlerMap = {}; | ||
packages.forEach(function (pkg) { | ||
pkg.processors.forEach(function (processorName) { | ||
var processor = injector_1.get(processorName); | ||
// Update the processor's name and package | ||
processor.name = processorName; | ||
processor.$package = pkg.name; | ||
// Ignore disabled processors | ||
if (processor.$enabled !== false) { | ||
processorMap_1[processorName] = processor; | ||
} | ||
}); | ||
var _loop_1 = function (eventName) { | ||
var handlers = _this.handlerMap[eventName] = (_this.handlerMap[eventName] || []); | ||
pkg.handlers[eventName].forEach(function (handlerName) { return handlers.push(injector_1.get(handlerName)); }); | ||
}; | ||
for (var eventName in pkg.handlers) { | ||
_loop_1(eventName); | ||
} | ||
}); | ||
// Once we have configured everything sort the processors. | ||
// This allows the config blocks to modify the $runBefore and $runAfter properties of processors. | ||
// (Crazy idea, I know, but useful for things like debugDumpProcessor) | ||
this.processors = dependency_sort_1.sortByDependency(processorMap_1, '$runAfter', '$runBefore'); | ||
} | ||
}); | ||
}); | ||
} | ||
return this.injector; | ||
}; | ||
/** | ||
* Generate the documentation using the loaded packages | ||
* @return {Promise} A promise to the generated documents | ||
*/ | ||
Dgeni.prototype.generate = function() { | ||
this.configureInjector(); | ||
var dgeniConfig = this.injector.get('dgeni'); | ||
// Once we have configured everything sort the processors. | ||
// This allows the config blocks to modify the $runBefore and $runAfter | ||
// properties of processors. | ||
// (Crazy idea, I know, but useful for things like debugDumpProcessor) | ||
processors = sortByDependency(this.processors, '$runAfter', '$runBefore'); | ||
var log = this.injector.get('log'); | ||
var processingPromise = Q(); | ||
var validationErrors = []; | ||
// Apply the validations on each processor | ||
_.forEach(processors, function(processor) { | ||
processingPromise = processingPromise.then(function() { | ||
return validate.async(processor, processor.$validate).catch(function(errors) { | ||
validationErrors.push({ | ||
processor: processor.name, | ||
package: processor.$package, | ||
errors: errors | ||
return this.injector; | ||
}; | ||
/** | ||
* Generate the documentation using the loaded packages | ||
* @return {Promise} A promise to the generated documents | ||
*/ | ||
Dgeni.prototype.generate = function () { | ||
var _this = this; | ||
var injector = this.configureInjector(); | ||
var log = injector.get('log'); | ||
var processingPromise = this.triggerEvent('generationStart'); | ||
// Process the docs | ||
var currentDocs = []; | ||
processingPromise = processingPromise.then(function () { return currentDocs; }); | ||
this.processors.forEach(function (processor) { | ||
processingPromise = processingPromise.then(function (docs) { return _this.runProcessor(processor, docs); }); | ||
}); | ||
log.error('Invalid property in "' + processor.name + '" (in "' + processor.$package + '" package)'); | ||
log.error(errors); | ||
}); | ||
}); | ||
}); | ||
processingPromise = processingPromise.then(function() { | ||
if ( validationErrors.length > 0 && dgeniConfig.stopOnValidationError ) { | ||
return Q.reject(validationErrors); | ||
} | ||
}); | ||
// Process the docs | ||
var currentDocs = []; | ||
processingPromise = processingPromise.then(function() { | ||
return currentDocs; | ||
}); | ||
_.forEach(processors, function(processor) { | ||
if( processor.$process ) { | ||
processingPromise = processingPromise.then(function(docs) { | ||
currentDocs = docs; | ||
log.info('running processor:', processor.name); | ||
return Q(currentDocs).then(function() { | ||
// We need to wrap this $process call in a new promise handler so that we can catch | ||
// errors triggered by exceptions thrown in the $process method | ||
// before they reach the processingPromise handlers | ||
return processor.$process(docs) || docs; | ||
}).catch(function(error) { | ||
error.message = 'Error running processor "' + processor.name + '":\n' + error.message; | ||
if ( error.stack ) { | ||
log.error(error.stack); | ||
} | ||
if ( dgeniConfig.stopOnProcessingError ) { return Q.reject(error); } | ||
return currentDocs; | ||
processingPromise.catch(function (error) { return log.error('Error processing docs: ', error.stack || error.message || error); }); | ||
return processingPromise.then(function (docs) { | ||
_this.triggerEvent('generationEnd'); | ||
return docs; | ||
}); | ||
}); | ||
} | ||
return currentDocs; | ||
}); | ||
processingPromise.catch(function(error) { | ||
log.error('Error processing docs: ', error ); | ||
}); | ||
return processingPromise; | ||
}; | ||
/** | ||
* @module Dgeni | ||
*/ | ||
}; | ||
Dgeni.prototype.runProcessor = function (processor, docs) { | ||
var _this = this; | ||
var log = this.injector.get('log'); | ||
var promise = Q(docs); | ||
if (!processor.$process) { | ||
return promise; | ||
} | ||
return promise | ||
.then(function () { | ||
log.info('running processor:', processor.name); | ||
return _this.triggerProcessorEvent('processorStart', processor, docs); | ||
}) | ||
.then(function (docs) { return processor.$process(docs) || docs; }) | ||
.then(function (docs) { return _this.triggerProcessorEvent('processorEnd', processor, docs); }) | ||
.catch(function (error) { | ||
error.message = 'Error running processor "' + processor.name + '":\n' + error.message; | ||
log.error(error.stack || error.message); | ||
if (_this.stopOnProcessingError) { | ||
return Q.reject(error); | ||
} | ||
return docs; | ||
}); | ||
}; | ||
/** | ||
* Trigger a dgeni event and run all the registered handlers | ||
* All the arguments to this call are passed through to each handler | ||
* @param {string} eventName The event being triggered | ||
* @return {Promise} A promise to an array of the results from each of the handlers | ||
*/ | ||
Dgeni.prototype.triggerEvent = function (eventName) { | ||
var extras = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
extras[_i - 1] = arguments[_i]; | ||
} | ||
var handlers = this.handlerMap[eventName]; | ||
var handlersPromise = Q(); | ||
var results = []; | ||
if (handlers) { | ||
handlers.forEach(function (handler) { | ||
handlersPromise = handlersPromise.then(function () { | ||
var handlerPromise = Q(handler.apply(void 0, [eventName].concat(extras))); | ||
handlerPromise.then(function (result) { return results.push(result); }); | ||
return handlerPromise; | ||
}); | ||
}); | ||
} | ||
return handlersPromise.then(function () { return results; }); | ||
}; | ||
Dgeni.prototype.triggerProcessorEvent = function (eventName, processor, docs) { | ||
return this.triggerEvent(eventName, processor, docs).then(function () { return docs; }); | ||
}; | ||
Dgeni.prototype.info = function () { | ||
var injector = this.configureInjector(); | ||
var log = injector.get('log'); | ||
for (var pkgName in this.packages) { | ||
log.info(pkgName, '[' + this.packages[pkgName].dependencies.map(function (dep) { return JSON.stringify(dep.name); }).join(', ') + ']'); | ||
} | ||
log.info('== Processors (processing order) =='); | ||
this.processors.forEach(function (processor, index) { | ||
log.info((index + 1) + ': ' + processor.name, processor.$process ? '' : '(abstract)', ' from ', processor.$package); | ||
if (processor.description) { | ||
log.info(' ', processor.description); | ||
} | ||
}); | ||
}; | ||
; | ||
return Dgeni; | ||
}()); | ||
Dgeni.Package = Package_1.Package; | ||
exports.Dgeni = Dgeni; | ||
// This is a hack so that you can still require Dgeni | ||
// in CommonJS as: `var Dgeni = require('dgeni');` | ||
module.exports = Dgeni; | ||
module.exports.Dgeni = Dgeni; | ||
//# sourceMappingURL=Dgeni.js.map |
@@ -1,4 +0,2 @@ | ||
var _ = require('lodash'); | ||
var Module = require('di').Module; | ||
"use strict"; | ||
/** | ||
@@ -10,151 +8,187 @@ * A Dgeni Package containing processors, services and config blocks. | ||
*/ | ||
function Package(name, dependencies) { | ||
if ( typeof name !== 'string' ) { throw new Error('You must provide a name for the package'); } | ||
if ( dependencies && !Array.isArray(dependencies) ) { throw new Error('dependencies must be an array'); } | ||
this.name = name; | ||
this.dependencies = dependencies || []; | ||
this.processors = []; | ||
this.configFns = []; | ||
// We can't use a real di.Module here as di uses instanceof to detect module instances | ||
this.module = {}; | ||
var Package = (function () { | ||
function Package(name, dependencies) { | ||
if (dependencies === void 0) { dependencies = []; } | ||
this.name = name; | ||
this.dependencies = dependencies; | ||
this.processors = []; | ||
this.configFns = []; | ||
this.handlers = {}; | ||
// We can't use a real di.Module here as di uses instanceof to detect module instances | ||
this.module = {}; | ||
if (typeof name !== 'string') { | ||
throw new Error('You must provide a name for the package'); | ||
} | ||
if (dependencies && !Array.isArray(dependencies)) { | ||
throw new Error('dependencies must be an array'); | ||
} | ||
} | ||
Package.isPackage = function (pkg) { | ||
// Check the most important properties for dgeni to use a package | ||
return isString(pkg.name) && Array.isArray(pkg.dependencies) && isObject(pkg.module); | ||
}; | ||
/** | ||
* Add a new processor to the package. The processor can be defined by a processor definition object | ||
* or a factory function, which can be injected with services and will return the processor definition | ||
* object. The following properties of a processor definition object have special meaning to Dgeni: | ||
* | ||
* * `name : {string}`: The name of the processor - if the processor is defined by a factory | ||
* function or a name is explicitly provided as the first parameter, then this is ignored | ||
* * `$process(docs : {string}) : {Array|Promise|undefined}`: The method that will be called to | ||
* process the documents. If it is async then it should return a Promise. | ||
* * `$runAfter : {string[]}`: Dgeni will ensure that this processor runs after those named here. | ||
* * `$runBefore : {string[]}`: Dgeni will ensure that this processor runs before those named here. | ||
* * `$validate: {Object}`: Dgeni will check that the properties of the processor, which match the | ||
* keys of this object, pass the validation rules provided as the values of this object. See | ||
* http://validatejs.org | ||
* | ||
* @param {function|object|string} processorDefOrName | ||
* If this parameter is a string then it will be used as the processor's name, otherwise it is | ||
* assumed that it used as the `processorDef` | ||
* | ||
* @param {function|object} processorDef | ||
* The factory function or object that will be used by the injector to create the processor. | ||
* * If a function then it is a factory and it must not be anonymous - it must have a name, e.g. | ||
* `function myProcessor(dep1, dep2) { ... }` - since the name of the processor is taken from the | ||
* name of the factory function. | ||
* * If an object, then it is the actual processor and must have a `name` property. In this case, | ||
* you cannot inject services into this processor. | ||
* | ||
* @return {Package} | ||
* `this` package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.processor = function (processorDefOrName, processorDef) { | ||
var name; | ||
if (isString(processorDefOrName)) { | ||
name = processorDefOrName; | ||
} | ||
else { | ||
processorDef = processorDefOrName; | ||
// Using `any` here because Functions do usually have names (if they are not anonymous) | ||
if (!processorDef.name) { | ||
throw new Error('processorDef must be an object or a function with a name'); | ||
} | ||
name = processorDef.name; | ||
} | ||
if (typeof processorDef === 'function') { | ||
this.module[name] = ['factory', processorDef]; | ||
} | ||
else { | ||
this.module[name] = ['value', processorDef]; | ||
} | ||
this.processors.push(name); | ||
return this; | ||
}; | ||
/** | ||
* Add a new service, defined by a factory function, to the package | ||
* | ||
* @param {function|string} serviceFactoryOrName | ||
* If a string then this is the name of the service, otherwise it is assumed to be the | ||
* `serviceFactory`. | ||
* | ||
* @param {function} serviceFactory | ||
* The factory function that will be used by the injector to create the service. The function must | ||
* not be anonymous - it must have a name, e.g. `function myService() { ... }` - since the name of | ||
* the service is taken from the name of the factory function. | ||
* | ||
* @return {Package} | ||
* "This" package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.factory = function (serviceFactoryOrName, serviceFactory) { | ||
var name; | ||
if (typeof serviceFactoryOrName === 'string') { | ||
name = serviceFactoryOrName; | ||
} | ||
else { | ||
serviceFactory = serviceFactoryOrName; | ||
if (!serviceFactory.name) { | ||
throw new Error('serviceFactory must have a name'); | ||
} | ||
name = serviceFactory.name; | ||
} | ||
if (typeof serviceFactory !== 'function') { | ||
throw new Error('serviceFactory must be a function.\nGot "' + typeof serviceFactory + '"'); | ||
} | ||
this.module[name] = ['factory', serviceFactory]; | ||
return this; | ||
}; | ||
/** | ||
* Add a new service, defined as a Type to instantiated, to the package | ||
* | ||
* @param {function|string} ServiceTypeOrName | ||
* If a string then this is the name of the service, otherwise it is assumed to be the | ||
* `ServiceType`. | ||
* | ||
* @param {function} ServiceType | ||
* The constructor function that will be used by the injector to create the processor. The function | ||
* must not be anonymous - it must have a name, e.g. `function MyType() { ... }` - since the name of | ||
* the service is taken from the name of the constructor function. | ||
* | ||
* @return {Package} | ||
* "This" package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.type = function (ServiceTypeOrName, ServiceType) { | ||
var name; | ||
if (typeof ServiceTypeOrName === 'string') { | ||
name = ServiceTypeOrName; | ||
} | ||
else { | ||
ServiceType = ServiceTypeOrName; | ||
if (!ServiceType.name) { | ||
throw new Error('ServiceType must have a name'); | ||
} | ||
name = ServiceType.name; | ||
} | ||
if (typeof ServiceType !== 'function') { | ||
throw new Error('ServiceType must be a constructor function'); | ||
} | ||
this.module[name] = ['type', ServiceType]; | ||
return this; | ||
}; | ||
/** | ||
* Add a new config block to the package. Config blocks are run at the beginning of the doc | ||
* generation before the processors are run. They can be injected with services and processors | ||
* to allow you access to their properties so that you can configure them. | ||
* | ||
* @param {function} configFn The config block function to run | ||
* | ||
* @return {Package} | ||
* "This" package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.config = function (configFn) { | ||
if (typeof configFn !== 'function') { | ||
throw new Error('configFn must be a function'); | ||
} | ||
this.configFns.push(configFn); | ||
return this; | ||
}; | ||
/** | ||
* Add an event handler to this package | ||
* @param {string} eventName The name of the event to handle | ||
* @param {function} handlerFactory An injectable factory function that will return the handler | ||
* @return {Package} This package for chaining | ||
*/ | ||
Package.prototype.eventHandler = function (eventName, handlerFactory) { | ||
if (typeof eventName !== 'string') { | ||
throw new Error('You must provide a string identifying the type of event to handle'); | ||
} | ||
if (typeof handlerFactory !== 'function') { | ||
throw new Error('handlerFactory must be a function.\nGot "' + typeof handlerFactory + '"'); | ||
} | ||
var handlers = this.handlers[eventName] = this.handlers[eventName] || []; | ||
var handlerName = handlerFactory.name || (this.name + '_' + eventName + '_' + handlers.length); | ||
this.factory(handlerName, handlerFactory); | ||
handlers.push(handlerName); | ||
return this; | ||
}; | ||
return Package; | ||
}()); | ||
exports.Package = Package; | ||
function isObject(value) { | ||
var type = typeof value; | ||
return !!value && (type === 'object' || type === 'function'); | ||
} | ||
/** | ||
* Add a new processor to the package. The processor can be defined by a processor definition object | ||
* or a factory function, which can be injected with services and will return the processor definition | ||
* object. The following properties of a processor definition object have special meaning to Dgeni: | ||
* | ||
* * `name : {string}`: The name of the processor - if the processor is defined by a factory | ||
* function or a name is explicitly provided as the first parameter, then this is ignored | ||
* * `$process(docs : {string}) : {Array|Promise|undefined}`: The method that will be called to | ||
* process the documents. If it is async then it should return a Promise. | ||
* * `$runAfter : {string[]}`: Dgeni will ensure that this processor runs after those named here. | ||
* * `$runBefore : {string[]}`: Dgeni will ensure that this processor runs before those named here. | ||
* * `$validate: {Object}`: Dgeni will check that the properties of the processor, which match the | ||
* keys of this object, pass the validation rules provided as the values of this object. See | ||
* http://validatejs.org | ||
* | ||
* @param {function|object|string} processorDefOrName | ||
* If this parameter is a string then it will be used as the processor's name, otherwise it is | ||
* assumed that it used as the `processorDef` | ||
* | ||
* @param {function|object} processorDef | ||
* The factory function or object that will be used by the injector to create the processor. | ||
* * If a function then it is a factory and it must not be anonymous - it must have a name, e.g. | ||
* `function myProcessor(dep1, dep2) { ... }` - since the name of the processor is taken from the | ||
* name of the factory function. | ||
* * If an object, then it is the actual processor and must have a `name` property. In this case, | ||
* you cannot inject services into this processor. | ||
* | ||
* @return {Package} | ||
* `this` package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.processor = function(processorDefOrName, processorDef) { | ||
var name; | ||
if( typeof processorDefOrName === 'string' ) { | ||
name = processorDefOrName; | ||
} else { | ||
processorDef = processorDefOrName; | ||
if (!processorDef.name) { throw new Error('processorDef must be an object or a function with a name'); } | ||
name = processorDef.name; | ||
} | ||
if (typeof processorDef === 'function' ) { | ||
this.module[name] = ['factory', processorDef]; | ||
} else { | ||
this.module[name] = ['value', processorDef]; | ||
} | ||
this.processors.push(name); | ||
return this; | ||
}; | ||
/** | ||
* Add a new service, defined by a factory function, to the package | ||
* | ||
* @param {function|string} serviceFactoryOrName | ||
* If a string then this is the name of the service, otherwise it is assumed to be the | ||
* `serviceFactory`. | ||
* | ||
* @param {function} serviceFactory | ||
* The factory function that will be used by the injector to create the service. The function must | ||
* not be anonymous - it must have a name, e.g. `function myService() { ... }` - since the name of | ||
* the service is taken from the name of the factory function. | ||
* | ||
* @return {Package} | ||
* "This" package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.factory = function(serviceFactoryOrName, serviceFactory) { | ||
var name; | ||
if ( typeof serviceFactoryOrName === 'string' ) { | ||
name = serviceFactoryOrName; | ||
} else { | ||
serviceFactory = serviceFactoryOrName; | ||
if (!serviceFactory.name) { throw new Error('serviceFactory must have a name'); } | ||
name = serviceFactory.name; | ||
} | ||
if (typeof serviceFactory !== 'function' ) { | ||
throw new Error('serviceFactory must be a function.\nGot "' + typeof serviceFactory + '"'); | ||
} | ||
this.module[name] = ['factory', serviceFactory]; | ||
return this; | ||
}; | ||
/** | ||
* Add a new service, defined as a Type to instantiated, to the package | ||
* | ||
* @param {function|string} ServiceTypeOrName | ||
* If a string then this is the name of the service, otherwise it is assumed to be the | ||
* `ServiceType`. | ||
* | ||
* @param {function} ServiceType | ||
* The constructor function that will be used by the injector to create the processor. The function | ||
* must not be anonymous - it must have a name, e.g. `function MyType() { ... }` - since the name of | ||
* the service is taken from the name of the constructor function. | ||
* | ||
* @return {Package} | ||
* "This" package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.type = function(ServiceTypeOrName, ServiceType) { | ||
var name; | ||
if ( typeof ServiceTypeOrName === 'string' ) { | ||
name = ServiceTypeOrName; | ||
} else { | ||
ServiceType = ServiceTypeOrName; | ||
if (!ServiceType.name) { throw new Error('ServiceType must have a name'); } | ||
name = ServiceType.name; | ||
} | ||
if (typeof ServiceType !== 'function' ) { throw new Error('ServiceType must be a constructor function'); } | ||
this.module[name] = ['type', ServiceType]; | ||
return this; | ||
}; | ||
/** | ||
* Add a new config block to the package. Config blocks are run at the beginning of the doc | ||
* generation before the processors are run. They can be injected with services and processors | ||
* to allow you access to their properties so that you can configure them. | ||
* | ||
* @param {function} configFn The config block function to run | ||
* | ||
* @return {Package} | ||
* "This" package, to allow methods to be chained. | ||
*/ | ||
Package.prototype.config = function(configFn) { | ||
if (typeof configFn !== 'function' ) { throw new Error('configFn must be a function'); } | ||
this.configFns.push(configFn); | ||
return this; | ||
}; | ||
Package.isPackage = function(package) { | ||
// Check the most important properties for dgeni to use a package | ||
return _.isString(package.name) && _.isArray(package.dependencies) && _.isObject(package.module); | ||
}; | ||
/** | ||
* @module Package | ||
*/ | ||
module.exports = Package; | ||
function isString(value) { | ||
return typeof value === 'string'; | ||
} | ||
//# sourceMappingURL=Package.js.map |
@@ -0,4 +1,4 @@ | ||
"use strict"; | ||
var _ = require('lodash'); | ||
var DepGraph = require('dependency-graph').DepGraph; | ||
/** | ||
@@ -19,44 +19,42 @@ * @name sortByDependency | ||
*/ | ||
module.exports = function sortByDependency(items, afterProp, beforeProp, nameProp) { | ||
nameProp = nameProp || 'name'; | ||
var map = {}; | ||
var depGraph = new DepGraph(); | ||
var addDependencies = function(item, dependencyProp, addBefore) { | ||
if ( dependencyProp && item[dependencyProp]) { | ||
if ( !Array.isArray(item[dependencyProp]) ) { | ||
throw new Error('Error in item "' + item[nameProp] + '" - ' + dependencyProp + ' must be an array'); | ||
} | ||
item[dependencyProp].forEach(function(dependency) { | ||
if ( !map[dependency] ) { | ||
throw new Error('Missing dependency: "' + dependency + '" on "' + item[nameProp] + '"'); | ||
function sortByDependency(items, afterProp, beforeProp, nameProp) { | ||
if (nameProp === void 0) { nameProp = 'name'; } | ||
var map = {}; | ||
var depGraph = new DepGraph(); | ||
var addDependencies = function (item, dependencyProp, addBefore) { | ||
if (addBefore === void 0) { addBefore = false; } | ||
if (dependencyProp && item[dependencyProp]) { | ||
if (!Array.isArray(item[dependencyProp])) { | ||
throw new Error('Error in item "' + item[nameProp] + '" - ' + dependencyProp + ' must be an array'); | ||
} | ||
item[dependencyProp].forEach(function (dependency) { | ||
if (!map[dependency]) { | ||
throw new Error('Missing dependency: "' + dependency + '" on "' + item[nameProp] + '"'); | ||
} | ||
if (addBefore) { | ||
depGraph.addDependency(dependency, item[nameProp]); | ||
} | ||
else { | ||
depGraph.addDependency(item[nameProp], dependency); | ||
} | ||
}); | ||
} | ||
if ( addBefore ) { | ||
depGraph.addDependency(dependency, item[nameProp]); | ||
} else { | ||
depGraph.addDependency(item[nameProp], dependency); | ||
}; | ||
_.forEach(items, function (item, index) { | ||
if (!item[nameProp]) { | ||
throw new Error('Missing ' + nameProp + ' property on item #' + (index + 1)); | ||
} | ||
}); | ||
} | ||
}; | ||
_.forEach(items, function(item, index) { | ||
if ( !item[nameProp] ) { | ||
throw new Error('Missing ' + nameProp + ' property on item #' + (index+1)); | ||
} | ||
map[item[nameProp]] = item; | ||
depGraph.addNode(item[nameProp]); | ||
}); | ||
_.forEach(items, function(item) { | ||
addDependencies(item, afterProp); | ||
addDependencies(item, beforeProp, true); | ||
}); | ||
return depGraph.overallOrder().map(function(itemName) { | ||
return map[itemName]; | ||
}); | ||
}; | ||
map[item[nameProp]] = item; | ||
depGraph.addNode(item[nameProp]); | ||
}); | ||
_.forEach(items, function (item) { | ||
addDependencies(item, afterProp); | ||
addDependencies(item, beforeProp, true); | ||
}); | ||
return depGraph.overallOrder().map(function (itemName) { | ||
return map[itemName]; | ||
}); | ||
} | ||
exports.sortByDependency = sortByDependency; | ||
; | ||
//# sourceMappingURL=dependency-sort.js.map |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -7,10 +8,15 @@ * @dgService getInjectables | ||
*/ | ||
module.exports = function getInjectables(injector) { | ||
return function(factories) { | ||
return factories.map(function(factory) { | ||
var instance = injector.invoke(factory); | ||
instance.name = instance.name || factory.name; | ||
return instance; | ||
}); | ||
}; | ||
}; | ||
function getInjectablesFactory(injector) { | ||
return function (factories) { | ||
return factories.map(function (factory) { | ||
var instance = injector.invoke(factory); | ||
if (!instance.name) { | ||
instance.name = factory.name; | ||
} | ||
return instance; | ||
}); | ||
}; | ||
} | ||
exports.getInjectablesFactory = getInjectablesFactory; | ||
; | ||
//# sourceMappingURL=getInjectables.js.map |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -7,7 +8,10 @@ * @dgService log | ||
*/ | ||
module.exports = function logFactory() { | ||
var winston = require('winston'); | ||
winston.cli(); | ||
winston.level = 'info'; | ||
return winston; | ||
}; | ||
function logFactory() { | ||
var winston = require('winston'); | ||
winston.cli(); | ||
winston.level = 'info'; | ||
return winston; | ||
} | ||
exports.logFactory = logFactory; | ||
; | ||
//# sourceMappingURL=log.js.map |
{ | ||
"name": "dgeni", | ||
"version": "0.4.2", | ||
"description": "Flexible JavaScript documentation generator used by AngularJS", | ||
"version": "0.4.3", | ||
"description": "Flexible JavaScript documentation generator used by Angular", | ||
"main": "lib/Dgeni.js", | ||
"types": "lib/index.d.ts", | ||
"bin": { | ||
"dgeni": "lib/gen-docs.js" | ||
}, | ||
"files": [ | ||
"lib" | ||
], | ||
"scripts": { | ||
"test": "jasmine-node lib", | ||
"cover": "istanbul cover jasmine-node -- lib", | ||
"docs": "bin/gen-docs.js ./docs/dgeni-docs.js" | ||
"prepublish": "npm build", | ||
"prebuild": "rm -rf lib", | ||
"build": "tsc", | ||
"watch": "tsc -w", | ||
"test": "mocha --compilers ts:ts-node/register -R spec src/**/*.spec.ts", | ||
"docs": "node lib/gen-docs.js ./docs/dgeni-docs.js" | ||
}, | ||
"bin": { | ||
"dgeni": "bin/gen-docs.js" | ||
}, | ||
"repository": { | ||
@@ -19,4 +26,5 @@ "type": "git", | ||
"author": "Pete Bacon Darwin", | ||
"license": "Apache-2.0", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@types/mocha": "^2.2.39", | ||
"canonical-path": "~0.0.2", | ||
@@ -26,2 +34,3 @@ "dependency-graph": "~0.4.1", | ||
"lodash": "^3.10.1", | ||
"objectdiff": "^1.1.0", | ||
"optimist": "~0.6.1", | ||
@@ -33,6 +42,11 @@ "q": "~1.4.1", | ||
"devDependencies": { | ||
"dgeni-packages": "^0.11.1", | ||
"istanbul": "^0.4.1", | ||
"jasmine-node": "^2.0.0", | ||
"rewire": "~2.5.1" | ||
"@types/chai": "^3.4.34", | ||
"@types/node": "^7.0.5", | ||
"chai": "^3.5.0", | ||
"chai-spies": "^0.7.1", | ||
"dgeni-packages": "^0.16.0", | ||
"mocha": "^3.2.0", | ||
"ts-node": "^2.1.0", | ||
"tslint": "^4.4.2", | ||
"typescript": "^2.1.6" | ||
}, | ||
@@ -39,0 +53,0 @@ "contributors": [ |
@@ -18,2 +18,13 @@ # Dgeni - Documentation Generator [![Build Status](https://travis-ci.org/angular/dgeni.svg?branch=master)](https://travis-ci.org/angular/dgeni) | ||
## Documenting Angular 1 Apps | ||
There are two projects out there that build upon dgeni to help create documentation for Angular 1 apps: | ||
* dgeni-alive: https://github.com/wingedfox/dgeni-alive | ||
* sia: https://github.com/boundstate/sia | ||
Do check them out and thanks to [Ilya](https://github.com/wingedfox) and [Bound State Software](https://github.com/boundstate) | ||
for putting these projects together. | ||
## Installation | ||
@@ -208,2 +219,6 @@ | ||
**Note that the validation feature has been moved to its own Dgeni Package `processorValidation`. | ||
Currently dgeni automatically adds this new package to a new instance of dgeni so that is still available | ||
for backward compatibility. In a future release this package will be moved to `dgeni-packages`.** | ||
### Defining a Processor | ||
@@ -279,2 +294,5 @@ | ||
* `typescript` - depends upon `base` and adds Processors and Services to support parsing and | ||
extracting jsdoc style tags from comments in TypeScript (*.ts) code. | ||
* `nunjucks` - provides a [nunjucks](http://mozilla.github.io/nunjucks/) based rendering | ||
@@ -340,4 +358,58 @@ engine. | ||
## Dgeni Events | ||
In Dgeni you can trigger and handle **events** to allow packages to take part in the processing | ||
lifecycle of the documentation generation. | ||
### Triggering Events | ||
You trigger an event simply by calling `triggerEvent(eventName, ...)` on a `Dgeni` instance. | ||
The `eventName` is a string that identifies the event to be triggered, which is used to wire up | ||
event handlers. Additional arguments are passed through to the handlers. | ||
Each handler that is registered for the event is called in series. The return value | ||
from the call is a promise to the event being handled. This allows event handlers to be async. | ||
If any handler returns a rejected promise the event triggering is cancelled and the rejected | ||
promise is returned. | ||
For example: | ||
```js | ||
var eventPromise = dgeni.triggerEvent('someEventName', someArg, otherArg); | ||
``` | ||
### Handling Events | ||
You register an event handler in a `Package`, by calling `handleEvent(eventName, handlerFactory)` on | ||
the package instance. The handlerFactory will be used by the DI system to get the handler, which allows | ||
you to inject services to be available to the handler. | ||
The handler factory should return the handler function. This function will receive all the arguments passed | ||
to the `triggerHandler` method. As a minimum this will include the `eventName`. | ||
For example: | ||
```js | ||
myPackage.eventHandler('generationStart', function validateProcessors(log, dgeni) { | ||
return function validateProcessorsImpl(eventName) { | ||
... | ||
}; | ||
}); | ||
``` | ||
### Built-in Events | ||
Dgeni itself triggers the following events during documentation generation: | ||
* `generationStart`: triggered after the injector has been configured and before the processors begin | ||
their work. | ||
* `generationEnd`: triggered after the processors have all completed their work successfully. | ||
* `processorStart`: triggered just before the call to `$process`, for each processor. | ||
* `processorEnd`: triggered just after `$process` has completed successfully, for each processor. | ||
## License | ||
Apache 2 |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
412
82285
10
9
46
915
1
+ Added@types/mocha@^2.2.39
+ Addedobjectdiff@^1.1.0
+ Added@types/mocha@2.2.48(transitive)
+ Addedobjectdiff@1.1.0(transitive)