diff --git a/lib/yuidoc.js b/lib/yuidoc.js index 3d354047..80519338 100755 --- a/lib/yuidoc.js +++ b/lib/yuidoc.js @@ -245,8 +245,48 @@ YUI.add('yuidoc', function (Y) { } }, + /** + * Applies preprocessors to the data tree. + * This function first clones the data and operates on the clone. + * @method runPreprocessors + * @private + * @return {Object} The mutated data + */ + runPreprocessors: function (data) { + var self = this, + preprocessors, + preprocessorsRelativeTo; + + // We will try to load the preprocessors as npm modules, but we will also + // search for them relative to the process working directory. + // The latter is consistent with how other paths are treated by yuidoc, + // such as the config options 'paths' and 'outdir'. + preprocessorsRelativeTo = process.cwd(); + + if (self.options.preprocessor) { + data=JSON.parse(JSON.stringify(data)); + + preprocessors = [].concat(self.options.preprocessor); + + preprocessors.forEach(function (preprocessor) { + var preprocessorModule; + + try { + preprocessorModule = require(preprocessor); + } catch (e) { + preprocessorModule = require(path.resolve(preprocessorsRelativeTo, preprocessor)); + } + + preprocessorModule(data, self.options); + }); + } + + return data; + }, + /** * Writes the parser JSON data to disk. + * Applies preprocessors, if any. * @method writeJSON * @param {Object} parser The DocParser instance to use * @private @@ -262,6 +302,8 @@ YUI.add('yuidoc', function (Y) { data.warnings = parser.warnings; + data = this.runPreprocessors(data); + if (self.selleck && self.options.selleck && data.files && data.modules) { Object.keys(self.selleck).forEach(function (file) { Object.keys(data.files).forEach(function (f) { diff --git a/package.json b/package.json index e16856aa..50ef23b9 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ }, "scripts": { "pretest": "jshint ./lib/*.js ./tests/*.js", - "test": "istanbul cover --print=both --yui ytestrunner -- --include ./tests/options.js --include ./tests/builder.js --include ./tests/parser.js --include ./tests/parser_coffee.js --include ./tests/files.js --include ./tests/utils.js" + "test": "istanbul cover --print=both --yui ytestrunner -- --include ./tests/options.js --include ./tests/builder.js --include ./tests/parser.js --include ./tests/parser_coffee.js --include ./tests/files.js --include ./tests/utils.js --include ./tests/preprocessor.js" }, "preferGlobal": "true", "licenses":[ diff --git a/scripts/test.sh b/scripts/test.sh index 94ffe494..6839f92a 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -19,7 +19,8 @@ wait ./builder.js \ ./options.js \ ./utils.js \ - ./files.js + ./files.js \ + ./preprocessor.js exit $? diff --git a/tests/input/preprocessor/preprocessortest.js b/tests/input/preprocessor/preprocessortest.js new file mode 100644 index 00000000..314fec3c --- /dev/null +++ b/tests/input/preprocessor/preprocessortest.js @@ -0,0 +1,5 @@ +/** + * This class is for testing the preprocessor option. + * @class TestPreprocessor + * @customtag hello + */ diff --git a/tests/lib/testpreprocessor.js b/tests/lib/testpreprocessor.js new file mode 100644 index 00000000..09bbf011 --- /dev/null +++ b/tests/lib/testpreprocessor.js @@ -0,0 +1,14 @@ +module.exports=function(data, options) { + var star="*"; + + if (options.star) + star=options.star; + + global.testPreprocessorCallCount++; + + for (className in data.classes) { + classData=data.classes[className]; + + classData.customtagPlusStar=classData.customtag+star; + } +} diff --git a/tests/lib/testpreprocessormodule/package.json b/tests/lib/testpreprocessormodule/package.json new file mode 100644 index 00000000..e00fa501 --- /dev/null +++ b/tests/lib/testpreprocessormodule/package.json @@ -0,0 +1,3 @@ +{ + "main": "./testpreprocessormodule.js" +} diff --git a/tests/lib/testpreprocessormodule/testpreprocessormodule.js b/tests/lib/testpreprocessormodule/testpreprocessormodule.js new file mode 100644 index 00000000..001e814c --- /dev/null +++ b/tests/lib/testpreprocessormodule/testpreprocessormodule.js @@ -0,0 +1,3 @@ +module.exports=function(data) { + data.testModuleWasHere=true; +} diff --git a/tests/preprocessor.js b/tests/preprocessor.js new file mode 100644 index 00000000..ab78ea68 --- /dev/null +++ b/tests/preprocessor.js @@ -0,0 +1,101 @@ +/*global Y:true */ +var YUITest = require('yuitest'), + Assert = YUITest.Assert, + path = require('path'), + fs = require('fs'), + Y = require(path.join(__dirname, '../', 'lib', 'index')); + +//Move to the test dir before running the tests. +process.chdir(__dirname); + +var suite = new YUITest.TestSuite({ + name: 'Preprocessor Test Suite', + setUp: function () { + process.chdir(__dirname); + } +}); + +suite.add(new YUITest.TestCase({ + name: "Preprocessor", + 'test: single preprocessor': function () { + global.testPreprocessorCallCount=0; + + var json = (new Y.YUIDoc({ + quiet: true, + paths: ['input/preprocessor'], + outdir: './out', + preprocessor: 'lib/testpreprocessor.js' + })).run(); + + Assert.isObject(json); + Assert.areSame(global.testPreprocessorCallCount,1,"the preprocessor was not called"); + }, + 'test: single preprocessor with absolute path': function () { + global.testPreprocessorCallCount=0; + + var json = (new Y.YUIDoc({ + quiet: true, + paths: ['input/preprocessor'], + outdir: './out', + preprocessor: path.join(process.cwd(),'/lib/testpreprocessor.js') + })).run(); + + Assert.isObject(json); + Assert.areSame(global.testPreprocessorCallCount,1,"the preprocessor was not called when an absolute path was used"); + }, + 'test: several preprocessors': function () { + global.testPreprocessorCallCount=0; + + var json = (new Y.YUIDoc({ + quiet: true, + paths: ['input/preprocessor'], + outdir: './out', + preprocessor: ['lib/testpreprocessor.js','./lib/testpreprocessor'] + })).run(); + + Assert.isObject(json); + Assert.areSame(global.testPreprocessorCallCount,2,"the preprocessor was not called twice"); + }, + 'test: the test preprocessor does its job': function () { + var json = (new Y.YUIDoc({ + quiet: true, + paths: ['input/preprocessor'], + outdir: './out', + preprocessor: 'lib/testpreprocessor.js', + star: "#" + })).run(); + + Assert.isObject(json); + Assert.areSame(json.classes.TestPreprocessor.customtagPlusStar,"hello#","the preprocessor did not modify the data"); + }, + 'test: load preprocessor as a npm module': function () { + // We are testing if it works to load the preprocessor from node_modules, + // so first we need to copy it in place. + if (!fs.existsSync("../node_modules/testpreprocessormodule")) { + fs.mkdirSync("../node_modules/testpreprocessormodule"); + } + + fs.writeFileSync("../node_modules/testpreprocessormodule/package.json", + fs.readFileSync("lib/testpreprocessormodule/package.json")); + + fs.writeFileSync("../node_modules/testpreprocessormodule/testpreprocessormodule.js", + fs.readFileSync("lib/testpreprocessormodule/testpreprocessormodule.js")); + + var json = (new Y.YUIDoc({ + quiet: true, + paths: ['input/preprocessor'], + outdir: './out', + preprocessor: 'testpreprocessormodule' + })).run(); + + Assert.isObject(json); + Assert.isTrue(json.testModuleWasHere,"the preprocesor module was not run"); + + // Clean up things when we are done. + fs.unlinkSync("../node_modules/testpreprocessormodule/package.json"); + fs.unlinkSync("../node_modules/testpreprocessormodule/testpreprocessormodule.js"); + fs.rmdirSync("../node_modules/testpreprocessormodule"); + } +})); + +YUITest.TestRunner.add(suite);