Add JSDoc annotation

Some minor changes regarding `var compile` is needed to make JSDoc
recognize the function.
This commit is contained in:
Timothy Gu
2015-01-24 20:20:01 -08:00
parent 87d26c0a71
commit 319cac097c
5 changed files with 351 additions and 23 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ test/tmp
coverage/
/ejs.js
/ejs.min.js
out/

15
jsdoc.json Normal file
View File

@@ -0,0 +1,15 @@
{
"_comment": "Configuration file for JSDoc."
, "tags": {
"allowUnknownTags": true
}
, "source": {
"includePattern": ".+\\.js(doc)?$"
, "excludePattern": "(^|\\/|\\\\)_"
}
, "plugins": [ "plugins/markdown" ]
, "templates": {
"cleverLinks": true
, "monospaceLinks": false
}
}

View File

@@ -18,6 +18,32 @@
'use strict';
/**
* @file Embedded JavaScript templating engine.
* @author Matthew Eernisse <mde@fleegix.org>
* @author Tiancheng "Timothy" Gu <timothygu99@gmail.com>
*/
/**
* EJS internal functions.
*
* Technically this "module" lies in the same file as {@link module:ejs}, for
* the sake of organization all the private functions re grouped into this
* module.
*
* @module ejs-internal
* @private
*/
/**
* @file Embedded JavaScript templating engine.
*
* @project EJS
* @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0}
* @module ejs
* @public
*/
var fs = require('fs')
, utils = require('./utils')
, jsCache = {}
@@ -30,8 +56,29 @@ var fs = require('fs')
]
, _TRAILING_SEMCOL = /;\s*$/;
/**
* Name of the object containing the locals.
*
* This variable is overriden by {@link Options}`.localsName` if it is not
* `undefined`.
*
* @type {String}
* @public
*/
exports.localsName = _DEFAULT_LOCALS_NAME;
/**
* Get the path to the included file from the parent file path and the
* specified path.
*
* @memberof module:ejs-internal
* @param {String} name specified path
* @param {String} filename parent file path
* @return {String}
* @static
*/
function resolveInclude(name, filename) {
var path = require('path')
, dirname = path.dirname
@@ -45,10 +92,24 @@ function resolveInclude(name, filename) {
return includePath;
}
// Returns a possibly cached template function, set by options.cache.
// `template` is the string of EJS to compile.
// If template is undefined then the file specified in options.filename is
// read.
/**
* Get the template from a string or a file, either compiled on-the-fly or
* read from cache (if enabled), and cache the template if needed.
*
* If `template` is not set, the file specified in `options.filename` will be
* read.
*
* If `options.cache` is true, this function reads the file from
* `options.filename` so it must be set prior to calling this function.
*
* @memberof module:ejs-internal
* @param {Options} options compilation options
* @param {String} [template] template source
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `options.client`, either type might be returned.
* @static
*/
function handleCache(options, template) {
var fn
, path = options.filename
@@ -80,6 +141,19 @@ function handleCache(options, template) {
return fn;
}
/**
* Get the template function.
*
* If `options.cache` is `true`, then the template is cached.
*
* @memberof module:ejs-internal
* @param {String} path path for the specified file
* @param {Options} options compilation options
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `options.client`, either type might be returned
* @static
*/
function includeFile(path, options) {
var opts = utils.shallowCopy({}, options || /* istanbul ignore next */ {});
if (!opts.filename) {
@@ -89,6 +163,16 @@ function includeFile(path, options) {
return handleCache(opts);
}
/**
* Get the JavaScript source of an included file.
*
* @memberof module:ejs-internal
* @param {String} path path for the specified file
* @param {Options} options compilation options
* @return {String}
* @static
*/
function includeSource(path, options) {
var opts = utils.shallowCopy({}, options || {})
, includePath
@@ -105,6 +189,20 @@ function includeSource(path, options) {
return templ.source;
}
/**
* Re-throw the given `err` in context to the `str` of ejs, `filename`, and
* `lineno`.
*
* This implements {@link RethrowCallback}.
*
* @memberof module:ejs-internal
* @param {Error} err Error object
* @param {String} str EJS source
* @param {String} filename file name of the EJS file
* @param {String} lineno line number of the error
* @static
*/
function rethrow(err, str, filename, lineno){
var lines = str.split('\n')
, start = Math.max(lineno - 3, 0)
@@ -129,6 +227,18 @@ function rethrow(err, str, filename, lineno){
throw err;
}
/**
* Copy properties in data object that are recognized as options to an
* options object.
*
* This is used for compatibility with earlier versions of EJS and Express.js.
*
* @memberof module:ejs-internal
* @param {Object} data data object
* @param {Options} opts options object
* @static
*/
function cpOptsInData(data, opts) {
_OPTS.forEach(function (p) {
if (typeof data[p] != 'undefined') {
@@ -138,7 +248,117 @@ function cpOptsInData(data, opts) {
delete data.__expressRender__;
}
function compile(template, opts) {
/**
* Compilation and rendering options.
*
* @typedef Options
* @type {Object}
*
* @property {Boolean} [debug=false]
* Log generated JavaScript source for the EJS template to the console.
*
* @property {Boolean} [compileDebug=true]
* Include additional runtime debugging information in generated template
* functions.
*
* @property {Boolean} [_with=true]
* Whether or not to use `with () {}` construct in the generated template
* functions. If set to `false`, data is still accessible through the `locals`
* object.
*
* @property {Boolean} [localsName='locals']
* The name of the object containing the locals. Especially useful when
* `_with` is `false`. Overrides
*
* @property {Boolean} [client=false]
* Whether or not to compile functions that are to be included in the browser.
*
* @property {String} [filename='undefined']
* The filename of the template. Used in inclusion, caching, and error
* reporting.
* @property {String} [delimiter='%']
* The delimiter used in template compilation.
*
* @property {Boolean} [cache=false]
* Whether or not to enable caching of template functions. Beware that
* the options of compilation are not checked as being the same, so
* special handling is required if, for example, you want to cache client
* and regular functions of the same file.
*
* Requires `filename` to be set. Only works with rendering function.
*
* @property {Object} [context=this]
* The Object to which `this` is set during rendering.
*
* @property {Object} [scope=this]
* Alias of `context`. Deprecated.
*
* @static
* @global
*/
/**
* This type of function is returned from {@link module:ejs.compile}, when
* {@link Options}`.client` is false.
*
* @callback TemplateFunction
* @param {Object} [locals={}]
* an object of data to be passed into the template.
* @static
* @global
*/
/**
* This type of function is returned from {@link module:ejs.compile}, when
* {@link Options}`.client` is true.
*
* This is also used internally to generate a
* {@link TemplateFunction}.
*
* @callback ClientFunction
* @param {Object} [locals={}]
* an object of data to be passed into the template.
*
* @param {EscapeFunction} [escape={@link module:utils.escapeXML}]
* callback used to escape variables
*
* @param {IncludeFunction} [include]
* callback used to include files at runtime with `include()`
*
* @param {RethrowCallback} [rethrow={@link module:ejs-internal.rethrow}]
* callback used to handle and rethrow errors
*
* @static
* @global
*/
/**
* This type of callback is used when {@link Options}`.compileDebug`
* is true, and an error in the template is thrown. By default it is used to
* throw an error in a better-formatted way.
*
* @callback RethrowCallback
* @param {Error} err Error object
* @param {String} str EJS source
* @param {String} filename file name of the EJS file
* @param {String} lineno line number of the error
* @static
* @global
*/
/**
* Compile the given `str` of ejs into a template function.
*
* @param {String} template EJS template
*
* @param {Options} opts compilation options
*
* @return {(TemplateFunction|ClientFunction)}
* Depending on the value of `opts.client`, either type might be returned.
* @public
*/
exports.compile = function compile(template, opts) {
var templ;
// v1 compat
@@ -153,10 +373,20 @@ function compile(template, opts) {
templ = new Template(template, opts);
return templ.compile();
}
exports.compile = compile;
// template, [data], [opts]
// Have to include an empty data object if you want opts and no data
/**
* Render the given `template` of ejs.
*
* If you would like to include options but not data, you need to explicitly
* call this function with `data` being an empty object or `null`.
*
* @param {String} template EJS template
* @param {Object} [data={}] template data
* @param {Options} [opts={}] compilation and rendering options
* @return {String}
* @public
*/
exports.render = function (template, data, opts) {
data = data || {};
opts = opts || {};
@@ -172,8 +402,33 @@ exports.render = function (template, data, opts) {
return fn.call(opts.context, data);
};
// path, [data], [opts], cb
// Have to include an empty data object if you want opts and no data
/**
* Callback for receiving data from {@link module:ejs.renderFile}.
*
* @callback RenderFileCallback
* @param {?Error} err error, if any resulted from the rendering process
* @param {?String} str output string, is `null` if there is an error
* @static
* @global
*/
/**
* Render an EJS file at the given `path` and callback `cb(err, str)`.
*
* If you would like to include options but not data, you need to explicitly
* call this function with `data` being an empty object or `null`.
*
* Note that this function itself is *not* asynchronous, but the callback
* is called surrouded by `process.nextTick` so it behaves like an asynchronous
* call.
*
* @param {String} path path to the EJS file
* @param {Object} [data={}] template data
* @param {Options} [opts={}] compilation and rendering options
* @param {RenderFileCallback} cb callback
* @public
*/
exports.renderFile = function () {
var args = Array.prototype.slice.call(arguments)
, path = args.shift()
@@ -203,6 +458,11 @@ exports.renderFile = function () {
});
};
/**
* Clear intermediate JavaScript cache.
* @public
*/
exports.clearCache = function () {
jsCache = {};
};
@@ -229,11 +489,11 @@ function Template(text, opts) {
}
Template.modes = {
EVAL: 'eval'
, ESCAPED: 'escaped'
, RAW: 'raw'
, COMMENT: 'comment'
, LITERAL: 'literal'
EVAL: 'eval'
, ESCAPED: 'escaped'
, RAW: 'raw'
, COMMENT: 'comment'
, LITERAL: 'literal'
};
Template.prototype = {
@@ -490,7 +750,15 @@ Template.prototype = {
}
};
// Express support
/**
* Express.js support.
*
* This is an alias for {@link module:ejs.renderFile}, in order to support
* Express.js out-of-the-box.
*
* @func
*/
exports.__express = exports.renderFile;
// Add require support
@@ -503,11 +771,19 @@ if (require.extensions) {
, client: true
}
, template = fs.readFileSync(filename).toString().trim()
, fn = compile(template, options);
, fn = exports.compile(template, options);
module._compile('module.exports = ' + fn.toString() + ';', filename);
};
}
/**
* Version of EJS.
*
* @readonly
* @type {String}
* @public
*/
exports.VERSION = _VERSION_STRING;
/* istanbul ignore if */

View File

@@ -16,10 +16,25 @@
*
*/
/**
* Private utility functions
* @module utils
* @private
*/
'use strict';
var regExpChars = /[|\\{}()[\]^$+*?.]/g;
/**
* Escape characters reserved in regular expressions.
*
* If `string` is `undefined` or `null`, the empty string is returned.
*
* @param {String} string Input string
* @return {String} Escaped string
* @static
*/
exports.escapeRegExpChars = function (string) {
// istanbul ignore if
if (!string) {
@@ -28,6 +43,15 @@ exports.escapeRegExpChars = function (string) {
return String(string).replace(regExpChars, '\\$&');
};
/**
* Escape characters reserved in XML.
*
* If `markup` is `undefined` or `null`, the empty string is returned.
*
* @param {String} markup Input string
* @return {String} Escaped string
* @static
*/
exports.escapeXML = function (markup) {
// Handle stupid JS `undefined` and `null`
// Yes, we want double-equal here to catch both
@@ -43,10 +67,18 @@ exports.escapeXML = function (markup) {
.replace(/"/g, '&quot;');
};
/**
* Copy all properties from one object to another, in a shallow fashion.
*
* @param {Object} to Destination object
* @param {Object} from Source object
* @return {Object} Destination object
* @private
* @static
*/
exports.shallowCopy = function (to, from) {
for (var p in from) {
to[p] = from[p];
}
return to;
};

View File

@@ -22,16 +22,20 @@
"dependencies": {},
"devDependencies": {
"browserify": "^8.0.3",
"uglify-js": "^2.4.16",
"mocha": "^2.1.0",
"istanbul": "~0.3.5",
"jake": "^8.0.0",
"istanbul": "~0.3.5"
"jsdoc": "^3.3.0-beta1",
"mocha": "^2.1.0",
"rimraf": "^2.2.8",
"uglify-js": "^2.4.16"
},
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha",
"coverage": "istanbul cover node_modules/mocha/bin/_mocha"
"coverage": "istanbul cover node_modules/mocha/bin/_mocha",
"doc": "rimraf out && jsdoc -c jsdoc.json lib/*",
"devdoc": "rimraf out && jsdoc -p -c jsdoc.json lib/*"
}
}
}