updated npm

This commit is contained in:
Martin Donnelly 2017-07-18 12:42:39 +01:00
parent 8f674d16f8
commit 2a2ec3c100
35 changed files with 1982 additions and 1210 deletions

79
node_modules/ejs/Jakefile generated vendored
View File

@ -1,10 +1,12 @@
var fs = require('fs') var fs = require('fs');
, buildOpts = { var execSync = require('child_process').execSync;
printStdout: true var exec = function (cmd) {
, printStderr: true execSync(cmd, {stdio: 'inherit'});
}; };
task('build', ['browserify', 'minify'], function () { /* global jake, task, desc, publishTask */
task('build', ['lint', 'clean', 'browserify', 'minify'], function () {
console.log('Build completed.'); console.log('Build completed.');
}); });
@ -12,35 +14,58 @@ desc('Cleans browerified/minified files and package files');
task('clean', ['clobber'], function () { task('clean', ['clobber'], function () {
jake.rmRf('./ejs.js'); jake.rmRf('./ejs.js');
jake.rmRf('./ejs.min.js'); jake.rmRf('./ejs.min.js');
console.log('Cleaned up compiled files.');
}); });
task('browserify', {async: true}, function () { desc('Lints the source code');
jake.exec('./node_modules/browserify/bin/cmd.js lib/ejs.js > ejs.js', task('lint', function () {
buildOpts, function () { exec('./node_modules/.bin/eslint \"**/*.js\" Jakefile');
console.log('Browserification completed.'); console.log('Linting completed.');
setTimeout(complete, 0);
});
}); });
task('minify', {async: true}, function () { task('browserify', function () {
jake.exec('./node_modules/uglify-js/bin/uglifyjs ejs.js > ejs.min.js', exec('./node_modules/browserify/bin/cmd.js --standalone ejs lib/ejs.js > ejs.js');
buildOpts, function () { console.log('Browserification completed.');
console.log('Minification completed.'); });
setTimeout(complete, 0);
}); task('minify', function () {
exec('./node_modules/uglify-js/bin/uglifyjs ejs.js > ejs.min.js');
console.log('Minification completed.');
});
task('doc', function (dev) {
jake.rmRf('out');
var p = dev ? '-p' : '';
exec('./node_modules/.bin/jsdoc ' + p + ' -c jsdoc.json lib/* docs/jsdoc/*');
console.log('Documentation generated.');
});
task('docPublish', ['doc'], function () {
fs.writeFileSync('out/CNAME', 'api.ejs.co');
console.log('Pushing docs to gh-pages...');
exec('./node_modules/.bin/git-directory-deploy --directory out/');
console.log('Docs published to gh-pages.');
});
task('test', ['lint'], function () {
exec('./node_modules/.bin/mocha');
}); });
publishTask('ejs', ['build'], function () { publishTask('ejs', ['build'], function () {
this.packageFiles.include([ this.packageFiles.include([
'Jakefile' 'Jakefile',
, 'README.md' 'README.md',
, 'LICENSE' 'LICENSE',
, 'package.json' 'package.json',
, 'ejs.js' 'ejs.js',
, 'ejs.min.js' 'ejs.min.js',
, 'lib/**' 'lib/**',
, 'test/**' 'test/**'
]); ]);
}); });
jake.Task.publish.on('complete', function () {
console.log('Updating hosted docs...');
console.log('If this fails, run jake docPublish to re-try.');
jake.Task.docPublish.invoke();
});

25
node_modules/ejs/README.md generated vendored
View File

@ -3,7 +3,7 @@
Embedded JavaScript templates Embedded JavaScript templates
[![Build Status](https://img.shields.io/travis/mde/ejs/master.svg?style=flat)](https://travis-ci.org/mde/ejs) [![Build Status](https://img.shields.io/travis/mde/ejs/master.svg?style=flat)](https://travis-ci.org/mde/ejs)
[![Developing Dependencies](https://img.shields.io/david/dev/mde/ejs.svg?style=flat)](https://david-dm.org/mde/ejs#info=devDependencies) [![Developing Dependencies](https://img.shields.io/david/dev/mde/ejs.svg?style=flat)](https://david-dm.org/mde/ejs?type=dev)
## Installation ## Installation
@ -18,7 +18,7 @@ $ npm install ejs
* Unescaped raw output with `<%- %>` * Unescaped raw output with `<%- %>`
* Newline-trim mode ('newline slurping') with `-%>` ending tag * Newline-trim mode ('newline slurping') with `-%>` ending tag
* Whitespace-trim mode (slurp all whitespace) for control flow with `<%_ _%>` * Whitespace-trim mode (slurp all whitespace) for control flow with `<%_ _%>`
* Custom delimiters (e.g., use '<? ?>' instead of '<% %>') * Custom delimiters (e.g., use `<? ?>` instead of `<% %>`)
* Includes * Includes
* Client-side support * Client-side support
* Static caching of intermediate JavaScript * Static caching of intermediate JavaScript
@ -33,6 +33,8 @@ $ npm install ejs
<% } %> <% } %>
``` ```
Try EJS online at: https://ionicabizau.github.io/ejs-playground/.
## Usage ## Usage
```javascript ```javascript
@ -59,6 +61,7 @@ Therefore, we do not recommend using this shortcut.
- `cache` Compiled functions are cached, requires `filename` - `cache` Compiled functions are cached, requires `filename`
- `filename` The name of the file being rendered. Not required if you - `filename` The name of the file being rendered. Not required if you
are using `renderFile()`. Used by `cache` to key caches, and for includes. are using `renderFile()`. Used by `cache` to key caches, and for includes.
- `root` Set project root for includes with an absolute path (/file.ejs).
- `context` Function execution context - `context` Function execution context
- `compileDebug` When `false` no debug instrumentation is compiled - `compileDebug` When `false` no debug instrumentation is compiled
- `client` When `true`, compiles a function that can be rendered - `client` When `true`, compiles a function that can be rendered
@ -89,6 +92,7 @@ the both the public & private API docs, run `npm run devdoc` instead.
- `<%-` Outputs the unescaped value into the template - `<%-` Outputs the unescaped value into the template
- `<%#` Comment tag, no execution, no output - `<%#` Comment tag, no execution, no output
- `<%%` Outputs a literal '<%' - `<%%` Outputs a literal '<%'
- `%%>` Outputs a literal '%>'
- `%>` Plain ending tag - `%>` Plain ending tag
- `-%>` Trim-mode ('newline slurp') tag, trims following newline - `-%>` Trim-mode ('newline slurp') tag, trims following newline
- `_%>` 'Whitespace Slurping' ending tag, removes all whitespace after it - `_%>` 'Whitespace Slurping' ending tag, removes all whitespace after it
@ -158,6 +162,21 @@ If you want to clear the EJS cache, call `ejs.clearCache`. If you're using the
LRU cache and need a different limit, simple reset `ejs.cache` to a new instance LRU cache and need a different limit, simple reset `ejs.cache` to a new instance
of the LRU. of the LRU.
## Custom FileLoader
The default file loader is `fs.readFileSync`, if you want to customize it, you can set ejs.fileLoader.
```javascript
var ejs = require('ejs');
var myFileLoad = function (filePath) {
return 'myFileLoad: ' + fs.readFileSync(filePath);
};
ejs.fileLoader = myFileLoad;
```
With this feature, you can preprocess the template before reading it.
## Layouts ## Layouts
EJS does not specifically support blocks, but layouts can be implemented by EJS does not specifically support blocks, but layouts can be implemented by
@ -235,5 +254,3 @@ Licensed under the Apache License, Version 2.0
- - - - - -
EJS Embedded JavaScript templates copyright 2112 EJS Embedded JavaScript templates copyright 2112
mde@fleegix.org. mde@fleegix.org.

768
node_modules/ejs/ejs.js generated vendored

File diff suppressed because it is too large Load Diff

2
node_modules/ejs/ejs.min.js generated vendored

File diff suppressed because one or more lines are too long

554
node_modules/ejs/lib/ejs.js generated vendored
View File

@ -19,7 +19,7 @@
'use strict'; 'use strict';
/** /**
* @file Embedded JavaScript templating engine. * @file Embedded JavaScript templating engine. {@link http://ejs.co}
* @author Matthew Eernisse <mde@fleegix.org> * @author Matthew Eernisse <mde@fleegix.org>
* @author Tiancheng "Timothy" Gu <timothygu99@gmail.com> * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com>
* @project EJS * @project EJS
@ -44,19 +44,23 @@
* @public * @public
*/ */
var fs = require('fs') var fs = require('fs');
, utils = require('./utils') var path = require('path');
, scopeOptionWarned = false var utils = require('./utils');
, _VERSION_STRING = require('../package.json').version
, _DEFAULT_DELIMITER = '%' var scopeOptionWarned = false;
, _DEFAULT_LOCALS_NAME = 'locals' var _VERSION_STRING = require('../package.json').version;
, _REGEX_STRING = '(<%%|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)' var _DEFAULT_DELIMITER = '%';
, _OPTS = [ 'cache', 'filename', 'delimiter', 'scope', 'context' var _DEFAULT_LOCALS_NAME = 'locals';
, 'debug', 'compileDebug', 'client', '_with', 'rmWhitespace' var _NAME = 'ejs';
, 'strict', 'localsName' var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';
] var _OPTS = ['delimiter', 'scope', 'context', 'debug', 'compileDebug',
, _TRAILING_SEMCOL = /;\s*$/ 'client', '_with', 'rmWhitespace', 'strict', 'filename'];
, _BOM = /^\uFEFF/; // We don't allow 'cache' option to be passed in the data obj
// for the normal `render` call, but this is where Express puts it
// so we make an exception for `renderFile`
var _OPTS_EXPRESS = _OPTS.concat('cache');
var _BOM = /^\uFEFF/;
/** /**
* EJS template function cache. This can be a LRU object from lru-cache NPM * EJS template function cache. This can be a LRU object from lru-cache NPM
@ -68,10 +72,19 @@ var fs = require('fs')
exports.cache = utils.cache; exports.cache = utils.cache;
/**
* Custom file loader. Useful for template preprocessing or restricting access
* to a certain part of the filesystem.
*
* @type {fileLoader}
*/
exports.fileLoader = fs.readFileSync;
/** /**
* Name of the object containing the locals. * Name of the object containing the locals.
* *
* This variable is overriden by {@link Options}`.localsName` if it is not * This variable is overridden by {@link Options}`.localsName` if it is not
* `undefined`. * `undefined`.
* *
* @type {String} * @type {String}
@ -84,24 +97,44 @@ exports.localsName = _DEFAULT_LOCALS_NAME;
* Get the path to the included file from the parent file path and the * Get the path to the included file from the parent file path and the
* specified path. * specified path.
* *
* @param {String} name specified path * @param {String} name specified path
* @param {String} filename parent file path * @param {String} filename parent file path
* @param {Boolean} isDir parent file path whether is directory
* @return {String} * @return {String}
*/ */
exports.resolveInclude = function(name, filename, isDir) {
exports.resolveInclude = function(name, filename) { var dirname = path.dirname;
var path = require('path') var extname = path.extname;
, dirname = path.dirname var resolve = path.resolve;
, extname = path.extname var includePath = resolve(isDir ? filename : dirname(filename), name);
, resolve = path.resolve var ext = extname(name);
, includePath = resolve(dirname(filename), name)
, ext = extname(name);
if (!ext) { if (!ext) {
includePath += '.ejs'; includePath += '.ejs';
} }
return includePath; return includePath;
}; };
/**
* Get the path to the included file by Options
*
* @param {String} path specified path
* @param {Options} options compilation options
* @return {String}
*/
function getIncludePath(path, options){
var includePath;
if (path.charAt(0) == '/') {
includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true);
}
else {
if (!options.filename) {
throw new Error('`include` use relative path requires the \'filename\' option.');
}
includePath = exports.resolveInclude(path, options.filename);
}
return includePath;
}
/** /**
* Get the template from a string or a file, either compiled on-the-fly or * 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. * read from cache (if enabled), and cache the template if needed.
@ -121,35 +154,70 @@ exports.resolveInclude = function(name, filename) {
*/ */
function handleCache(options, template) { function handleCache(options, template) {
var fn var func;
, path = options.filename var filename = options.filename;
, hasTemplate = arguments.length > 1; var hasTemplate = arguments.length > 1;
if (options.cache) { if (options.cache) {
if (!path) { if (!filename) {
throw new Error('cache option requires a filename'); throw new Error('cache option requires a filename');
} }
fn = exports.cache.get(path); func = exports.cache.get(filename);
if (fn) { if (func) {
return fn; return func;
} }
if (!hasTemplate) { if (!hasTemplate) {
template = fs.readFileSync(path).toString().replace(_BOM, ''); template = fileLoader(filename).toString().replace(_BOM, '');
} }
} }
else if (!hasTemplate) { else if (!hasTemplate) {
// istanbul ignore if: should not happen at all // istanbul ignore if: should not happen at all
if (!path) { if (!filename) {
throw new Error('Internal EJS error: no file name or template ' throw new Error('Internal EJS error: no file name or template '
+ 'provided'); + 'provided');
} }
template = fs.readFileSync(path).toString().replace(_BOM, ''); template = fileLoader(filename).toString().replace(_BOM, '');
} }
fn = exports.compile(template, options); func = exports.compile(template, options);
if (options.cache) { if (options.cache) {
exports.cache.set(path, fn); exports.cache.set(filename, func);
} }
return fn; return func;
}
/**
* Try calling handleCache with the given options and data and call the
* callback with the result. If an error occurs, call the callback with
* the error. Used by renderFile().
*
* @memberof module:ejs-internal
* @param {Options} options compilation options
* @param {Object} data template data
* @param {RenderFileCallback} cb callback
* @static
*/
function tryHandleCache(options, data, cb) {
var result;
try {
result = handleCache(options)(data);
}
catch (err) {
return cb(err);
}
return cb(null, result);
}
/**
* fileLoader is independent
*
* @param {String} filePath ejs file path.
* @return {String} The contents of the specified file.
* @static
*/
function fileLoader(filePath){
return exports.fileLoader(filePath);
} }
/** /**
@ -167,10 +235,7 @@ function handleCache(options, template) {
function includeFile(path, options) { function includeFile(path, options) {
var opts = utils.shallowCopy({}, options); var opts = utils.shallowCopy({}, options);
if (!opts.filename) { opts.filename = getIncludePath(path, opts);
throw new Error('`include` requires the \'filename\' option.');
}
opts.filename = exports.resolveInclude(path, opts.filename);
return handleCache(opts); return handleCache(opts);
} }
@ -180,24 +245,24 @@ function includeFile(path, options) {
* @memberof module:ejs-internal * @memberof module:ejs-internal
* @param {String} path path for the specified file * @param {String} path path for the specified file
* @param {Options} options compilation options * @param {Options} options compilation options
* @return {String} * @return {Object}
* @static * @static
*/ */
function includeSource(path, options) { function includeSource(path, options) {
var opts = utils.shallowCopy({}, options) var opts = utils.shallowCopy({}, options);
, includePath var includePath;
, template; var template;
if (!opts.filename) { includePath = getIncludePath(path, opts);
throw new Error('`include` requires the \'filename\' option.'); template = fileLoader(includePath).toString().replace(_BOM, '');
}
includePath = exports.resolveInclude(path, opts.filename);
template = fs.readFileSync(includePath).toString().replace(_BOM, '');
opts.filename = includePath; opts.filename = includePath;
var templ = new Template(template, opts); var templ = new Template(template, opts);
templ.generateSource(); templ.generateSource();
return templ.source; return {
source: templ.source,
filename: includePath,
template: template
};
} }
/** /**
@ -213,11 +278,11 @@ function includeSource(path, options) {
* @static * @static
*/ */
function rethrow(err, str, filename, lineno){ function rethrow(err, str, flnm, lineno, esc){
var lines = str.split('\n') var lines = str.split('\n');
, start = Math.max(lineno - 3, 0) var start = Math.max(lineno - 3, 0);
, end = Math.min(lines.length, lineno + 3); var end = Math.min(lines.length, lineno + 3);
var filename = esc(flnm); // eslint-disable-line
// Error context // Error context
var context = lines.slice(start, end).map(function (line, i){ var context = lines.slice(start, end).map(function (line, i){
var curr = i + start + 1; var curr = i + start + 1;
@ -237,24 +302,8 @@ function rethrow(err, str, filename, lineno){
throw err; throw err;
} }
/** function stripSemi(str){
* Copy properties in data object that are recognized as options to an return str.replace(/;(\s*$)/, '$1');
* 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') {
opts[p] = data[p];
}
});
} }
/** /**
@ -302,15 +351,14 @@ exports.compile = function compile(template, opts) {
* @public * @public
*/ */
exports.render = function (template, data, opts) { exports.render = function (template, d, o) {
data = data || {}; var data = d || {};
opts = opts || {}; var opts = o || {};
var fn;
// No options object -- if there are optiony names // No options object -- if there are optiony names
// in the data, copy them to options // in the data, copy them to options
if (arguments.length == 2) { if (arguments.length == 2) {
cpOptsInData(data, opts); utils.shallowCopyFromList(opts, data, _OPTS);
} }
return handleCache(opts, template)(data); return handleCache(opts, template)(data);
@ -330,37 +378,38 @@ exports.render = function (template, data, opts) {
*/ */
exports.renderFile = function () { exports.renderFile = function () {
var args = Array.prototype.slice.call(arguments) var filename = arguments[0];
, path = args.shift() var cb = arguments[arguments.length - 1];
, cb = args.pop() var opts = {filename: filename};
, data = args.shift() || {} var data;
, opts = args.pop() || {}
, result;
// Don't pollute passed in opts obj with new vals if (arguments.length > 2) {
opts = utils.shallowCopy({}, opts); data = arguments[1];
// No options object -- if there are optiony names // No options object -- if there are optiony names
// in the data, copy them to options // in the data, copy them to options
if (arguments.length == 3) { if (arguments.length === 3) {
// Express 4 // Express 4
if (data.settings && data.settings['view options']) { if (data.settings && data.settings['view options']) {
cpOptsInData(data.settings['view options'], opts); utils.shallowCopyFromList(opts, data.settings['view options'], _OPTS_EXPRESS);
}
// Express 3 and lower
else {
utils.shallowCopyFromList(opts, data, _OPTS_EXPRESS);
}
} }
// Express 3 and lower
else { else {
cpOptsInData(data, opts); // Use shallowCopy so we don't pollute passed in opts obj with new vals
utils.shallowCopy(opts, arguments[2]);
} }
}
opts.filename = path;
try { opts.filename = filename;
result = handleCache(opts)(data);
} }
catch(err) { else {
return cb(err); data = {};
} }
return cb(null, result);
return tryHandleCache(opts, data, cb);
}; };
/** /**
@ -391,6 +440,7 @@ function Template(text, opts) {
options.context = opts.context; options.context = opts.context;
options.cache = opts.cache || false; options.cache = opts.cache || false;
options.rmWhitespace = opts.rmWhitespace; options.rmWhitespace = opts.rmWhitespace;
options.root = opts.root;
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME; options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
if (options.strict) { if (options.strict) {
@ -406,39 +456,28 @@ function Template(text, opts) {
} }
Template.modes = { Template.modes = {
EVAL: 'eval' EVAL: 'eval',
, ESCAPED: 'escaped' ESCAPED: 'escaped',
, RAW: 'raw' RAW: 'raw',
, COMMENT: 'comment' COMMENT: 'comment',
, LITERAL: 'literal' LITERAL: 'literal'
}; };
Template.prototype = { Template.prototype = {
createRegex: function () { createRegex: function () {
var str = _REGEX_STRING var str = _REGEX_STRING;
, delim = utils.escapeRegExpChars(this.opts.delimiter); var delim = utils.escapeRegExpChars(this.opts.delimiter);
str = str.replace(/%/g, delim); str = str.replace(/%/g, delim);
return new RegExp(str); return new RegExp(str);
} },
, compile: function () { compile: function () {
var src var src;
, fn var fn;
, opts = this.opts var opts = this.opts;
, prepended = '' var prepended = '';
, appended = '' var appended = '';
, escape = opts.escapeFunction; var escapeFn = opts.escapeFunction;
if (opts.rmWhitespace) {
// Have to use two separate replace here as `^` and `$` operators don't
// work well with `\r`.
this.templateText =
this.templateText.replace(/\r/g, '').replace(/^\s+|\s+$/gm, '');
}
// Slurp spaces and tabs before <%_ and after _%>
this.templateText =
this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>');
if (!this.source) { if (!this.source) {
this.generateSource(); this.generateSource();
@ -459,7 +498,7 @@ Template.prototype = {
+ 'try {' + '\n' + 'try {' + '\n'
+ this.source + this.source
+ '} catch (e) {' + '\n' + '} catch (e) {' + '\n'
+ ' rethrow(e, __lines, __filename, __line);' + '\n' + ' rethrow(e, __lines, __filename, __line, escapeFn);' + '\n'
+ '}' + '\n'; + '}' + '\n';
} }
else { else {
@ -471,7 +510,7 @@ Template.prototype = {
} }
if (opts.client) { if (opts.client) {
src = 'escape = escape || ' + escape.toString() + ';' + '\n' + src; src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src;
if (opts.compileDebug) { if (opts.compileDebug) {
src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src; src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
} }
@ -482,7 +521,7 @@ Template.prototype = {
} }
try { try {
fn = new Function(opts.localsName + ', escape, include, rethrow', src); fn = new Function(opts.localsName + ', escapeFn, include, rethrow', src);
} }
catch(e) { catch(e) {
// istanbul ignore else // istanbul ignore else
@ -490,7 +529,9 @@ Template.prototype = {
if (opts.filename) { if (opts.filename) {
e.message += ' in ' + opts.filename; e.message += ' in ' + opts.filename;
} }
e.message += ' while compiling ejs'; e.message += ' while compiling ejs\n\n';
e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n';
e.message += 'https://github.com/RyanZim/EJS-Lint';
} }
throw e; throw e;
} }
@ -511,28 +552,38 @@ Template.prototype = {
} }
return includeFile(path, opts)(d); return includeFile(path, opts)(d);
}; };
return fn.apply(opts.context, [data || {}, escape, include, rethrow]); return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]);
}; };
returnedFn.dependencies = this.dependencies; returnedFn.dependencies = this.dependencies;
return returnedFn; return returnedFn;
} },
, generateSource: function () { generateSource: function () {
var self = this var opts = this.opts;
, matches = this.parseTemplateText()
, d = this.opts.delimiter; if (opts.rmWhitespace) {
// Have to use two separate replace here as `^` and `$` operators don't
// work well with `\r`.
this.templateText =
this.templateText.replace(/\r/g, '').replace(/^\s+|\s+$/gm, '');
}
// Slurp spaces and tabs before <%_ and after _%>
this.templateText =
this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>');
var self = this;
var matches = this.parseTemplateText();
var d = this.opts.delimiter;
if (matches && matches.length) { if (matches && matches.length) {
if (this.opts.compileDebug && this.opts.filename) {
this.source = ' ; __lines = ' + JSON.stringify(this.templateText) + '\n';
this.source += ' ; __filename = "' + this.opts.filename.replace(/\\/g, '/') + '"\n';
}
matches.forEach(function (line, index) { matches.forEach(function (line, index) {
var opening var opening;
, closing var closing;
, include var include;
, includeOpts var includeOpts;
, includeSrc; var includeObj;
var includeSrc;
// If this is an opening tag, check for closing tags // If this is an opening tag, check for closing tags
// FIXME: May end up with some false positives here // FIXME: May end up with some false positives here
// Better to store modes as k/v with '<' + delimiter as key // Better to store modes as k/v with '<' + delimiter as key
@ -550,9 +601,23 @@ Template.prototype = {
// Must be in EVAL or RAW mode // Must be in EVAL or RAW mode
if (opening && (opening == '<' + d || opening == '<' + d + '-' || opening == '<' + d + '_')) { if (opening && (opening == '<' + d || opening == '<' + d + '-' || opening == '<' + d + '_')) {
includeOpts = utils.shallowCopy({}, self.opts); includeOpts = utils.shallowCopy({}, self.opts);
includeSrc = includeSource(include[1], includeOpts); includeObj = includeSource(include[1], includeOpts);
includeSrc = ' ; (function(){' + '\n' + includeSrc + if (self.opts.compileDebug) {
' ; })()' + '\n'; includeSrc =
' ; (function(){' + '\n'
+ ' var __line = 1' + '\n'
+ ' , __lines = ' + JSON.stringify(includeObj.template) + '\n'
+ ' , __filename = ' + JSON.stringify(includeObj.filename) + ';' + '\n'
+ ' try {' + '\n'
+ includeObj.source
+ ' } catch (e) {' + '\n'
+ ' rethrow(e, __lines, __filename, __line);' + '\n'
+ ' }' + '\n'
+ ' ; }).call(this)' + '\n';
}else{
includeSrc = ' ; (function(){' + '\n' + includeObj.source +
' ; }).call(this)' + '\n';
}
self.source += includeSrc; self.source += includeSrc;
self.dependencies.push(exports.resolveInclude(include[1], self.dependencies.push(exports.resolveInclude(include[1],
includeOpts.filename)); includeOpts.filename));
@ -563,19 +628,17 @@ Template.prototype = {
}); });
} }
} },
, parseTemplateText: function () { parseTemplateText: function () {
var str = this.templateText var str = this.templateText;
, pat = this.regex var pat = this.regex;
, result = pat.exec(str) var result = pat.exec(str);
, arr = [] var arr = [];
, firstPos var firstPos;
, lastPos;
while (result) { while (result) {
firstPos = result.index; firstPos = result.index;
lastPos = pat.lastIndex;
if (firstPos !== 0) { if (firstPos !== 0) {
arr.push(str.substring(0, firstPos)); arr.push(str.substring(0, firstPos));
@ -592,12 +655,12 @@ Template.prototype = {
} }
return arr; return arr;
} },
, scanLine: function (line) { scanLine: function (line) {
var self = this var self = this;
, d = this.opts.delimiter var d = this.opts.delimiter;
, newLineCount = 0; var newLineCount = 0;
function _addOutput() { function _addOutput() {
if (self.truncate) { if (self.truncate) {
@ -606,13 +669,12 @@ Template.prototype = {
// after the tag that the truncation mode replaces // after the tag that the truncation mode replaces
// Handle Win / Unix / old Mac linebreaks -- do the \r\n // Handle Win / Unix / old Mac linebreaks -- do the \r\n
// combo first in the regex-or // combo first in the regex-or
line = line.replace(/^(?:\r\n|\r|\n)/, '') line = line.replace(/^(?:\r\n|\r|\n)/, '');
self.truncate = false; self.truncate = false;
} }
else if (self.opts.rmWhitespace) { else if (self.opts.rmWhitespace) {
// Gotta be more careful here. // rmWhitespace has already removed trailing spaces, just need
// .replace(/^(\s*)\n/, '$1') might be more appropriate here but as // to remove linebreaks
// rmWhitespace already removes trailing spaces anyway so meh.
line = line.replace(/^\n/, ''); line = line.replace(/^\n/, '');
} }
if (!line) { if (!line) {
@ -635,73 +697,75 @@ Template.prototype = {
newLineCount = (line.split('\n').length - 1); newLineCount = (line.split('\n').length - 1);
switch (line) { switch (line) {
case '<' + d: case '<' + d:
case '<' + d + '_': case '<' + d + '_':
this.mode = Template.modes.EVAL; this.mode = Template.modes.EVAL;
break; break;
case '<' + d + '=': case '<' + d + '=':
this.mode = Template.modes.ESCAPED; this.mode = Template.modes.ESCAPED;
break; break;
case '<' + d + '-': case '<' + d + '-':
this.mode = Template.modes.RAW; this.mode = Template.modes.RAW;
break; break;
case '<' + d + '#': case '<' + d + '#':
this.mode = Template.modes.COMMENT; this.mode = Template.modes.COMMENT;
break; break;
case '<' + d + d: case '<' + d + d:
this.mode = Template.modes.LITERAL; this.mode = Template.modes.LITERAL;
this.source += ' ; __append("' + line.replace('<' + d + d, '<' + d) + '")' + '\n'; this.source += ' ; __append("' + line.replace('<' + d + d, '<' + d) + '")' + '\n';
break; break;
case d + '>': case d + d + '>':
case '-' + d + '>': this.mode = Template.modes.LITERAL;
case '_' + d + '>': this.source += ' ; __append("' + line.replace(d + d + '>', d + '>') + '")' + '\n';
if (this.mode == Template.modes.LITERAL) { break;
_addOutput(); case d + '>':
} case '-' + d + '>':
case '_' + d + '>':
if (this.mode == Template.modes.LITERAL) {
_addOutput();
}
this.mode = null; this.mode = null;
this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0; this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;
break; break;
default: default:
// In script mode, depends on type of tag // In script mode, depends on type of tag
if (this.mode) { if (this.mode) {
// If '//' is found without a line break, add a line break. // If '//' is found without a line break, add a line break.
switch (this.mode) { switch (this.mode) {
case Template.modes.EVAL: case Template.modes.EVAL:
case Template.modes.ESCAPED: case Template.modes.ESCAPED:
case Template.modes.RAW: case Template.modes.RAW:
if (line.lastIndexOf('//') > line.lastIndexOf('\n')) { if (line.lastIndexOf('//') > line.lastIndexOf('\n')) {
line += '\n'; line += '\n';
}
} }
switch (this.mode) { }
switch (this.mode) {
// Just executing code // Just executing code
case Template.modes.EVAL: case Template.modes.EVAL:
this.source += ' ; ' + line + '\n'; this.source += ' ; ' + line + '\n';
break; break;
// Exec, esc, and output // Exec, esc, and output
case Template.modes.ESCAPED: case Template.modes.ESCAPED:
this.source += ' ; __append(escape(' + this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n';
line.replace(_TRAILING_SEMCOL, '').trim() + '))' + '\n'; break;
break;
// Exec and output // Exec and output
case Template.modes.RAW: case Template.modes.RAW:
this.source += ' ; __append(' + this.source += ' ; __append(' + stripSemi(line) + ')' + '\n';
line.replace(_TRAILING_SEMCOL, '').trim() + ')' + '\n'; break;
break; case Template.modes.COMMENT:
case Template.modes.COMMENT:
// Do nothing // Do nothing
break; break;
// Literal <%% mode, append as raw output // Literal <%% mode, append as raw output
case Template.modes.LITERAL: case Template.modes.LITERAL:
_addOutput();
break;
}
}
// In string mode, just add the output
else {
_addOutput(); _addOutput();
break;
} }
}
// In string mode, just add the output
else {
_addOutput();
}
} }
if (self.opts.compileDebug && newLineCount) { if (self.opts.compileDebug && newLineCount) {
@ -711,9 +775,17 @@ Template.prototype = {
} }
}; };
/* /**
* Export the internal function for escaping XML so people * Escape characters reserved in XML.
* can use for manual escaping if needed *
* This is simply an export of {@link module:utils.escapeXML}.
*
* If `markup` is `undefined` or `null`, the empty string is returned.
*
* @param {String} markup Input string
* @return {String} Escaped string
* @public
* @func
* */ * */
exports.escapeXML = utils.escapeXML; exports.escapeXML = utils.escapeXML;
@ -731,14 +803,14 @@ exports.__express = exports.renderFile;
// Add require support // Add require support
/* istanbul ignore else */ /* istanbul ignore else */
if (require.extensions) { if (require.extensions) {
require.extensions['.ejs'] = function (module, filename) { require.extensions['.ejs'] = function (module, flnm) {
filename = filename || /* istanbul ignore next */ module.filename; var filename = flnm || /* istanbul ignore next */ module.filename;
var options = { var options = {
filename: filename filename: filename,
, client: true client: true
} };
, template = fs.readFileSync(filename).toString() var template = fileLoader(filename).toString();
, fn = exports.compile(template, options); var fn = exports.compile(template, options);
module._compile('module.exports = ' + fn.toString() + ';', filename); module._compile('module.exports = ' + fn.toString() + ';', filename);
}; };
} }
@ -753,6 +825,16 @@ if (require.extensions) {
exports.VERSION = _VERSION_STRING; exports.VERSION = _VERSION_STRING;
/**
* Name for detection of EJS.
*
* @readonly
* @type {String}
* @public
*/
exports.name = _NAME;
/* istanbul ignore if */ /* istanbul ignore if */
if (typeof window != 'undefined') { if (typeof window != 'undefined') {
window.ejs = exports; window.ejs = exports;

45
node_modules/ejs/lib/utils.js generated vendored
View File

@ -45,17 +45,17 @@ exports.escapeRegExpChars = function (string) {
}; };
var _ENCODE_HTML_RULES = { var _ENCODE_HTML_RULES = {
'&': '&amp;' '&': '&amp;',
, '<': '&lt;' '<': '&lt;',
, '>': '&gt;' '>': '&gt;',
, '"': '&#34;' '"': '&#34;',
, "'": '&#39;' "'": '&#39;'
} };
, _MATCH_HTML = /[&<>\'"]/g; var _MATCH_HTML = /[&<>\'"]/g;
function encode_char(c) { function encode_char(c) {
return _ENCODE_HTML_RULES[c] || c; return _ENCODE_HTML_RULES[c] || c;
}; }
/** /**
* Stringified version of constants used by {@link module:utils.escapeXML}. * Stringified version of constants used by {@link module:utils.escapeXML}.
@ -98,11 +98,13 @@ exports.escapeXML = function (markup) {
.replace(_MATCH_HTML, encode_char); .replace(_MATCH_HTML, encode_char);
}; };
exports.escapeXML.toString = function () { exports.escapeXML.toString = function () {
return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr;
}; };
/** /**
* Copy all properties from one object to another, in a shallow fashion. * Naive copy of properties from one object to another.
* Does not recurse into non-scalar properties
* Does not check to see if the property has a value before copying
* *
* @param {Object} to Destination object * @param {Object} to Destination object
* @param {Object} from Source object * @param {Object} from Source object
@ -118,6 +120,28 @@ exports.shallowCopy = function (to, from) {
return to; return to;
}; };
/**
* Naive copy of a list of key names, from one object to another.
* Only copies property if it is actually defined
* Does not recurse into non-scalar properties
*
* @param {Object} to Destination object
* @param {Object} from Source object
* @param {Array} list List of properties to copy
* @return {Object} Destination object
* @static
* @private
*/
exports.shallowCopyFromList = function (to, from, list) {
for (var i = 0; i < list.length; i++) {
var p = list[i];
if (typeof from[p] != 'undefined') {
to[p] = from[p];
}
}
return to;
};
/** /**
* Simple in-process cache implementation. Does not implement limits of any * Simple in-process cache implementation. Does not implement limits of any
* sort. * sort.
@ -138,4 +162,3 @@ exports.cache = {
this._data = {}; this._data = {};
} }
}; };

78
node_modules/ejs/package.json generated vendored
View File

@ -1,42 +1,49 @@
{ {
"_args": [ "_args": [
[ [
"ejs@*", {
"/Users/martin/newdev/Rinser" "name": "ejs",
"raw": "ejs@2.5.6",
"rawSpec": "2.5.6",
"scope": null,
"spec": "2.5.6",
"type": "version"
},
"/home/martind2000/dev/Rinser"
] ]
], ],
"_from": "ejs@*", "_from": "ejs@2.5.6",
"_id": "ejs@2.4.2", "_id": "ejs@2.5.6",
"_inCache": true, "_inCache": true,
"_installable": true, "_installable": true,
"_location": "/ejs", "_location": "/ejs",
"_nodeVersion": "4.4.4", "_nodeVersion": "6.9.1",
"_npmOperationalInternal": { "_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com", "host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/ejs-2.4.2.tgz_1464117640663_0.8193834638223052" "tmp": "tmp/ejs-2.5.6.tgz_1487277787176_0.4875628533773124"
}, },
"_npmUser": { "_npmUser": {
"email": "mde@fleegix.org", "email": "mde@fleegix.org",
"name": "mde" "name": "mde"
}, },
"_npmVersion": "2.15.1", "_npmVersion": "3.10.8",
"_phantomChildren": {}, "_phantomChildren": {},
"_requested": { "_requested": {
"name": "ejs", "name": "ejs",
"raw": "ejs@*", "raw": "ejs@2.5.6",
"rawSpec": "*", "rawSpec": "2.5.6",
"scope": null, "scope": null,
"spec": "*", "spec": "2.5.6",
"type": "range" "type": "version"
}, },
"_requiredBy": [ "_requiredBy": [
"/" "/"
], ],
"_resolved": "http://registry.npmjs.org/ejs/-/ejs-2.4.2.tgz", "_resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.6.tgz",
"_shasum": "7057eb4812958fb731841cd9ca353343efe597b1", "_shasum": "479636bfa3fe3b1debd52087f0acb204b4f19c88",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "ejs@*", "_spec": "ejs@2.5.6",
"_where": "/Users/martin/newdev/Rinser", "_where": "/home/martind2000/dev/Rinser",
"author": { "author": {
"email": "mde@fleegix.org", "email": "mde@fleegix.org",
"name": "Matthew Eernisse", "name": "Matthew Eernisse",
@ -47,47 +54,44 @@
}, },
"contributors": [ "contributors": [
{ {
"name": "Timothy Gu",
"email": "timothygu99@gmail.com", "email": "timothygu99@gmail.com",
"name": "Timothy Gu",
"url": "https://timothygu.github.io" "url": "https://timothygu.github.io"
} }
], ],
"dependencies": {}, "dependencies": {},
"description": "Embedded JavaScript templates", "description": "Embedded JavaScript templates",
"devDependencies": { "devDependencies": {
"browserify": "^8.0.3", "browserify": "^13.0.1",
"istanbul": "~0.3.5", "eslint": "^3.0.0",
"git-directory-deploy": "^1.5.1",
"istanbul": "~0.4.3",
"jake": "^8.0.0", "jake": "^8.0.0",
"jsdoc": "^3.3.0-beta1", "jsdoc": "^3.4.0",
"lru-cache": "^2.5.0", "lru-cache": "^4.0.1",
"mocha": "^2.1.0", "mocha": "^3.0.2",
"rimraf": "^2.2.8", "uglify-js": "^2.6.2"
"uglify-js": "^2.4.16"
}, },
"directories": {}, "directories": {},
"dist": { "dist": {
"shasum": "7057eb4812958fb731841cd9ca353343efe597b1", "shasum": "479636bfa3fe3b1debd52087f0acb204b4f19c88",
"tarball": "https://registry.npmjs.org/ejs/-/ejs-2.4.2.tgz" "tarball": "https://registry.npmjs.org/ejs/-/ejs-2.5.6.tgz"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
}, },
"homepage": "https://github.com/mde/ejs", "homepage": "https://github.com/mde/ejs",
"keywords": [ "keywords": [
"ejs", "template",
"engine", "engine",
"template" "ejs"
], ],
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/ejs.js", "main": "./lib/ejs.js",
"maintainers": [ "maintainers": [
{ {
"name": "tjholowaychuk", "email": "mde@fleegix.org",
"email": "tj@vision-media.ca" "name": "mde"
},
{
"name": "mde",
"email": "mde@fleegix.org"
} }
], ],
"name": "ejs", "name": "ejs",
@ -99,10 +103,10 @@
}, },
"scripts": { "scripts": {
"coverage": "istanbul cover node_modules/mocha/bin/_mocha", "coverage": "istanbul cover node_modules/mocha/bin/_mocha",
"devdoc": "rimraf out && jsdoc -p -c jsdoc.json lib/* docs/jsdoc/*", "devdoc": "jake doc[dev]",
"doc": "rimraf out && jsdoc -c jsdoc.json lib/* docs/jsdoc/*", "doc": "jake doc",
"sample": "npm install express && node sample/index.js", "lint": "eslint \"**/*.js\" Jakefile",
"test": "mocha" "test": "mocha"
}, },
"version": "2.4.2" "version": "2.5.6"
} }

345
node_modules/ejs/test/ejs.js generated vendored
View File

@ -1,15 +1,16 @@
/* jshint mocha: true */ /* jshint mocha: true */
/* eslint-env node, mocha */
/** /**
* Module dependencies. * Module dependencies.
*/ */
var ejs = require('..') var ejs = require('..');
, fs = require('fs') var fs = require('fs');
, read = fs.readFileSync var read = fs.readFileSync;
, assert = require('assert') var assert = require('assert');
, path = require('path') var path = require('path');
, LRU = require('lru-cache'); var LRU = require('lru-cache');
try { try {
fs.mkdirSync(__dirname + '/tmp'); fs.mkdirSync(__dirname + '/tmp');
@ -103,10 +104,28 @@ suite('ejs.compile(str, options)', function () {
delete ejs.delimiter; delete ejs.delimiter;
}); });
test('support custom escape function', function () {
var customEscape;
var fn;
customEscape = function customEscape(str) {
return !str ? '' : str.toUpperCase();
};
fn = ejs.compile('HELLO <%= name %>', {escape: customEscape});
assert.equal(fn({name: 'world'}), 'HELLO WORLD');
});
test('strict mode works', function () {
assert.equal(ejs.render(fixture('strict.ejs'), {}, {strict: true}), 'true');
});
});
suite('client mode', function () {
test('have a working client option', function () { test('have a working client option', function () {
var fn var fn;
, str var str;
, preFn; var preFn;
fn = ejs.compile('<p><%= foo %></p>', {client: true}); fn = ejs.compile('<p><%= foo %></p>', {client: true});
str = fn.toString(); str = fn.toString();
if (!process.env.running_under_istanbul) { if (!process.env.running_under_istanbul) {
@ -116,9 +135,9 @@ suite('ejs.compile(str, options)', function () {
}); });
test('support client mode without locals', function () { test('support client mode without locals', function () {
var fn var fn;
, str var str;
, preFn; var preFn;
fn = ejs.compile('<p><%= "foo" %></p>', {client: true}); fn = ejs.compile('<p><%= "foo" %></p>', {client: true});
str = fn.toString(); str = fn.toString();
if (!process.env.running_under_istanbul) { if (!process.env.running_under_istanbul) {
@ -129,27 +148,17 @@ suite('ejs.compile(str, options)', function () {
test('not include rethrow() in client mode if compileDebug is false', function () { test('not include rethrow() in client mode if compileDebug is false', function () {
var fn = ejs.compile('<p><%= "foo" %></p>', { var fn = ejs.compile('<p><%= "foo" %></p>', {
client: true client: true,
, compileDebug: false compileDebug: false
}); });
// There could be a `rethrow` in the function declaration // There could be a `rethrow` in the function declaration
assert((fn.toString().match(/rethrow/g) || []).length <= 1); assert((fn.toString().match(/rethrow/g) || []).length <= 1);
}); });
test('support custom escape function', function () {
var customEscape
, fn;
customEscape = function customEscape(str) {
return !str ? '' : str.toUpperCase();
};
fn = ejs.compile('HELLO <%= name %>', {escape: customEscape});
assert.equal(fn({name: 'world'}), 'HELLO WORLD');
});
test('support custom escape function in client mode', function () { test('support custom escape function in client mode', function () {
var customEscape var customEscape;
, fn var fn;
, str; var str;
customEscape = function customEscape(str) { customEscape = function customEscape(str) {
return !str ? '' : str.toUpperCase(); return !str ? '' : str.toUpperCase();
}; };
@ -157,10 +166,31 @@ suite('ejs.compile(str, options)', function () {
str = fn.toString(); str = fn.toString();
if (!process.env.running_under_istanbul) { if (!process.env.running_under_istanbul) {
eval('var preFn = ' + str); eval('var preFn = ' + str);
assert.equal(preFn({name: 'world'}), 'HELLO WORLD'); assert.equal(preFn({name: 'world'}), 'HELLO WORLD'); // eslint-disable-line no-undef
} }
}); });
test('escape filename in errors in client mode', function () {
assert.throws(function () {
var fn = ejs.compile('<% throw new Error("whoops"); %>', {client: true, filename: '<script>'});
fn();
}, /Error: &lt;script&gt;/);
});
});
/* Old API -- remove when this shim goes away */
suite('ejs.render(str, dataAndOpts)', function () {
test('render the template with data/opts passed together', function () {
assert.equal(ejs.render('<p><?= foo ?></p>', {foo: 'yay', delimiter: '?'}),
'<p>yay</p>');
});
test('disallow unsafe opts passed along in data', function () {
assert.equal(ejs.render('<p><?= locals.foo ?></p>',
// localsName should not get reset because it's blacklisted
{_with: false, foo: 'yay', delimiter: '?', localsName: '_'}),
'<p>yay</p>');
});
}); });
suite('ejs.render(str, data, opts)', function () { suite('ejs.render(str, data, opts)', function () {
@ -224,10 +254,10 @@ suite('ejs.render(str, data, opts)', function () {
}); });
test('support caching', function () { test('support caching', function () {
var file = __dirname + '/tmp/render.ejs' var file = __dirname + '/tmp/render.ejs';
, options = {cache: true, filename: file} var options = {cache: true, filename: file};
, out = ejs.render('<p>Old</p>', {}, options) var out = ejs.render('<p>Old</p>', {}, options);
, expected = '<p>Old</p>'; var expected = '<p>Old</p>';
assert.equal(out, expected); assert.equal(out, expected);
// Assert no change, still in cache // Assert no change, still in cache
out = ejs.render('<p>New</p>', {}, options); out = ejs.render('<p>New</p>', {}, options);
@ -235,11 +265,11 @@ suite('ejs.render(str, data, opts)', function () {
}); });
test('support LRU caching', function () { test('support LRU caching', function () {
var oldCache = ejs.cache var oldCache = ejs.cache;
, file = __dirname + '/tmp/render.ejs' var file = __dirname + '/tmp/render.ejs';
, options = {cache: true, filename: file} var options = {cache: true, filename: file};
, out var out;
, expected = '<p>Old</p>'; var expected = '<p>Old</p>';
// Switch to LRU // Switch to LRU
ejs.cache = LRU(); ejs.cache = LRU();
@ -255,8 +285,8 @@ suite('ejs.render(str, data, opts)', function () {
}); });
test('opts.context', function () { test('opts.context', function () {
var ctxt = {foo: 'FOO'} var ctxt = {foo: 'FOO'};
, out = ejs.render('<%= this.foo %>', {}, {context: ctxt}); var out = ejs.render('<%= this.foo %>', {}, {context: ctxt});
assert.equal(out, ctxt.foo); assert.equal(out, ctxt.foo);
}); });
}); });
@ -273,8 +303,8 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
}); });
test('accept locals', function(done) { test('accept locals', function(done) {
var data = {name: 'fonebone'} var data = {name: 'fonebone'};
, options = {delimiter: '$'}; var options = {delimiter: '$'};
ejs.renderFile('test/fixtures/user.ejs', data, options, function(err, html) { ejs.renderFile('test/fixtures/user.ejs', data, options, function(err, html) {
if (err) { if (err) {
return done(err); return done(err);
@ -285,11 +315,10 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
}); });
test('accept locals without using with() {}', function(done) { test('accept locals without using with() {}', function(done) {
var data = {name: 'fonebone'} var data = {name: 'fonebone'};
, options = {delimiter: '$', _with: false} var options = {delimiter: '$', _with: false};
, doneCount = 0; var doneCount = 0;
ejs.renderFile('test/fixtures/user-no-with.ejs', data, options, ejs.renderFile('test/fixtures/user-no-with.ejs', data, options, function(err, html) {
function(err, html) {
if (err) { if (err) {
if (doneCount === 2) { if (doneCount === 2) {
return; return;
@ -319,9 +348,9 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
}); });
test('not catch err thrown by callback', function(done) { test('not catch err thrown by callback', function(done) {
var data = {name: 'fonebone'} var data = {name: 'fonebone'};
, options = {delimiter: '$'} var options = {delimiter: '$'};
, counter = 0; var counter = 0;
var d = require('domain').create(); var d = require('domain').create();
d.on('error', function (err) { d.on('error', function (err) {
@ -336,8 +365,7 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
// domains. Have to make it async. Ticket closed because: "domains are // domains. Have to make it async. Ticket closed because: "domains are
// deprecated :D" // deprecated :D"
process.nextTick(function () { process.nextTick(function () {
ejs.renderFile('test/fixtures/user.ejs', data, options, ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) {
function(err) {
counter++; counter++;
if (err) { if (err) {
assert.notEqual(err.message, 'Exception in callback'); assert.notEqual(err.message, 'Exception in callback');
@ -350,9 +378,9 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
}); });
test('support caching', function (done) { test('support caching', function (done) {
var expected = '<p>Old</p>' var expected = '<p>Old</p>';
, file = __dirname + '/tmp/renderFile.ejs' var file = __dirname + '/tmp/renderFile.ejs';
, options = {cache: true}; var options = {cache: true};
fs.writeFileSync(file, '<p>Old</p>'); fs.writeFileSync(file, '<p>Old</p>');
ejs.renderFile(file, {}, options, function (err, out) { ejs.renderFile(file, {}, options, function (err, out) {
@ -375,8 +403,7 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
test('opts.context', function (done) { test('opts.context', function (done) {
var ctxt = {foo: 'FOO'}; var ctxt = {foo: 'FOO'};
ejs.renderFile('test/fixtures/with-context.ejs', {}, ejs.renderFile('test/fixtures/with-context.ejs', {}, {context: ctxt}, function(err, html) {
{context: ctxt}, function(err, html) {
if (err) { if (err) {
return done(err); return done(err);
} }
@ -389,10 +416,10 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
suite('cache specific', function () { suite('cache specific', function () {
test('`clearCache` work properly', function () { test('`clearCache` work properly', function () {
var expected = '<p>Old</p>' var expected = '<p>Old</p>';
, file = __dirname + '/tmp/clearCache.ejs' var file = __dirname + '/tmp/clearCache.ejs';
, options = {cache: true, filename: file} var options = {cache: true, filename: file};
, out = ejs.render('<p>Old</p>', {}, options); var out = ejs.render('<p>Old</p>', {}, options);
assert.equal(out, expected); assert.equal(out, expected);
ejs.clearCache(); ejs.clearCache();
@ -403,11 +430,11 @@ suite('cache specific', function () {
}); });
test('`clearCache` work properly, LRU', function () { test('`clearCache` work properly, LRU', function () {
var expected = '<p>Old</p>' var expected = '<p>Old</p>';
, oldCache = ejs.cache var oldCache = ejs.cache;
, file = __dirname + '/tmp/clearCache.ejs' var file = __dirname + '/tmp/clearCache.ejs';
, options = {cache: true, filename: file} var options = {cache: true, filename: file};
, out; var out;
ejs.cache = LRU(); ejs.cache = LRU();
@ -422,11 +449,11 @@ suite('cache specific', function () {
}); });
test('LRU with cache-size 1', function () { test('LRU with cache-size 1', function () {
var oldCache = ejs.cache var oldCache = ejs.cache;
, options var options;
, out var out;
, expected var expected;
, file; var file;
ejs.cache = LRU(1); ejs.cache = LRU(1);
@ -472,6 +499,11 @@ suite('<%', function () {
}); });
suite('<%=', function () { suite('<%=', function () {
test('should not throw an error with a // comment on the final line', function () {
assert.equal(ejs.render('<%=\n// a comment\nname\n// another comment %>', {name: '&nbsp;<script>'}),
'&amp;nbsp;&lt;script&gt;');
});
test('escape &amp;<script>', function () { test('escape &amp;<script>', function () {
assert.equal(ejs.render('<%= name %>', {name: '&nbsp;<script>'}), assert.equal(ejs.render('<%= name %>', {name: '&nbsp;<script>'}),
'&amp;nbsp;&lt;script&gt;'); '&amp;nbsp;&lt;script&gt;');
@ -501,6 +533,11 @@ suite('<%=', function () {
}); });
suite('<%-', function () { suite('<%-', function () {
test('should not throw an error with a // comment on the final line', function () {
assert.equal(ejs.render('<%-\n// a comment\nname\n// another comment %>', {name: '&nbsp;<script>'}),
'&nbsp;<script>');
});
test('not escape', function () { test('not escape', function () {
assert.equal(ejs.render('<%- name %>', {name: '<script>'}), assert.equal(ejs.render('<%- name %>', {name: '<script>'}),
'<script>'); '<script>');
@ -551,13 +588,13 @@ suite('-%>', function () {
}); });
test('works with unix style', function () { test('works with unix style', function () {
var content = "<ul><% -%>\n" var content = '<ul><% -%>\n'
+ "<% users.forEach(function(user){ -%>\n" + '<% users.forEach(function(user){ -%>\n'
+ "<li><%= user.name -%></li>\n" + '<li><%= user.name -%></li>\n'
+ "<% }) -%>\n" + '<% }) -%>\n'
+ "</ul><% -%>\n"; + '</ul><% -%>\n';
var expectedResult = "<ul><li>geddy</li>\n<li>neil</li>\n<li>alex</li>\n</ul>"; var expectedResult = '<ul><li>geddy</li>\n<li>neil</li>\n<li>alex</li>\n</ul>';
var fn; var fn;
fn = ejs.compile(content); fn = ejs.compile(content);
assert.equal(fn({users: users}), assert.equal(fn({users: users}),
@ -565,13 +602,13 @@ suite('-%>', function () {
}); });
test('works with windows style', function () { test('works with windows style', function () {
var content = "<ul><% -%>\r\n" var content = '<ul><% -%>\r\n'
+ "<% users.forEach(function(user){ -%>\r\n" + '<% users.forEach(function(user){ -%>\r\n'
+ "<li><%= user.name -%></li>\r\n" + '<li><%= user.name -%></li>\r\n'
+ "<% }) -%>\r\n" + '<% }) -%>\r\n'
+ "</ul><% -%>\r\n"; + '</ul><% -%>\r\n';
var expectedResult = "<ul><li>geddy</li>\r\n<li>neil</li>\r\n<li>alex</li>\r\n</ul>"; var expectedResult = '<ul><li>geddy</li>\r\n<li>neil</li>\r\n<li>alex</li>\r\n</ul>';
var fn; var fn;
fn = ejs.compile(content); fn = ejs.compile(content);
assert.equal(fn({users: users}), assert.equal(fn({users: users}),
@ -591,6 +628,15 @@ suite('<%%', function () {
}); });
}); });
suite('%%>', function () {
test('produce literal', function () {
assert.equal(ejs.render('%%>'),
'%>');
assert.equal(ejs.render(' >', {}, {delimiter: ' '}),
' >');
});
});
suite('<%_ and _%>', function () { suite('<%_ and _%>', function () {
test('slurps spaces and tabs', function () { test('slurps spaces and tabs', function () {
assert.equal(ejs.render(fixture('space-and-tab-slurp.ejs'), {users: users}), assert.equal(ejs.render(fixture('space-and-tab-slurp.ejs'), {users: users}),
@ -656,8 +702,8 @@ suite('exceptions', function () {
var unhook = null; var unhook = null;
test('log JS source when debug is set', function (done) { test('log JS source when debug is set', function (done) {
var out = '' var out = '';
, needToExit = false; var needToExit = false;
unhook = hook_stdio(process.stdout, function (str) { unhook = hook_stdio(process.stdout, function (str) {
out += str; out += str;
if (needToExit) { if (needToExit) {
@ -672,6 +718,22 @@ suite('exceptions', function () {
}); });
ejs.render(fixture('hello-world.ejs'), {}, {debug: true}); ejs.render(fixture('hello-world.ejs'), {}, {debug: true});
}); });
test('escape filename in errors', function () {
assert.throws(function () {
ejs.render('<% throw new Error("whoops"); %>', {}, {filename: '<script>'});
}, /Error: &lt;script&gt;/);
});
test('filename in errors uses custom escape', function () {
assert.throws(function () {
ejs.render('<% throw new Error("whoops"); %>', {}, {
filename: '<script>',
escape: function () { return 'zooby'; }
});
}, /Error: zooby/);
});
teardown(function() { teardown(function() {
if (!unhook) { if (!unhook) {
return; return;
@ -727,6 +789,13 @@ suite('include()', function () {
fixture('include.html')); fixture('include.html'));
}); });
test('include ejs with set root path', function () {
var file = 'test/fixtures/include-root.ejs';
var viewsPath = path.join(__dirname, 'fixtures');
assert.equal(ejs.render(fixture('include-root.ejs'), {pets: users}, {filename: file, delimiter: '@',root:viewsPath}),
fixture('include.html'));
});
test('work when nested', function () { test('work when nested', function () {
var file = 'test/fixtures/menu.ejs'; var file = 'test/fixtures/menu.ejs';
assert.equal(ejs.render(fixture('menu.ejs'), {pets: users}, {filename: file}), assert.equal(ejs.render(fixture('menu.ejs'), {pets: users}, {filename: file}),
@ -734,8 +803,8 @@ suite('include()', function () {
}); });
test('work with a variable path', function () { test('work with a variable path', function () {
var file = 'test/fixtures/menu_var.ejs', var file = 'test/fixtures/menu_var.ejs';
includePath = 'includes/menu-item'; var includePath = 'includes/menu-item';
assert.equal(ejs.render(fixture('menu.ejs'), {pets: users, varPath: includePath}, {filename: file}), assert.equal(ejs.render(fixture('menu.ejs'), {pets: users, varPath: includePath}, {filename: file}),
fixture('menu.html')); fixture('menu.html'));
}); });
@ -747,12 +816,12 @@ suite('include()', function () {
}); });
test('pass compileDebug to include', function () { test('pass compileDebug to include', function () {
var file = 'test/fixtures/include.ejs' var file = 'test/fixtures/include.ejs';
, fn; var fn;
fn = ejs.compile(fixture('include.ejs'), { fn = ejs.compile(fixture('include.ejs'), {
filename: file filename: file,
, delimiter: '@' delimiter: '@',
, compileDebug: false compileDebug: false
}); });
try { try {
// Render without a required variable reference // Render without a required variable reference
@ -768,9 +837,9 @@ suite('include()', function () {
test('is dynamic', function () { test('is dynamic', function () {
fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>'); fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_cache.ejs' var file = 'test/fixtures/include_cache.ejs';
, options = {filename: file} var options = {filename: file};
, out = ejs.compile(fixture('include_cache.ejs'), options); var out = ejs.compile(fixture('include_cache.ejs'), options);
assert.equal(out(), '<p>Old</p>\n'); assert.equal(out(), '<p>Old</p>\n');
fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>New</p>'); fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>New</p>');
@ -779,10 +848,10 @@ suite('include()', function () {
test('support caching', function () { test('support caching', function () {
fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>'); fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_cache.ejs' var file = 'test/fixtures/include_cache.ejs';
, options = {cache: true, filename: file} var options = {cache: true, filename: file};
, out = ejs.render(fixture('include_cache.ejs'), {}, options) var out = ejs.render(fixture('include_cache.ejs'), {}, options);
, expected = fixture('include_cache.html'); var expected = fixture('include_cache.html');
assert.equal(out, expected); assert.equal(out, expected);
out = ejs.render(fixture('include_cache.ejs'), {}, options); out = ejs.render(fixture('include_cache.ejs'), {}, options);
// No change, still in cache // No change, still in cache
@ -830,8 +899,8 @@ suite('preprocessor include', function () {
}); });
test('tracks dependency correctly', function () { test('tracks dependency correctly', function () {
var file = 'test/fixtures/menu_preprocessor.ejs' var file = 'test/fixtures/menu_preprocessor.ejs';
, fn = ejs.compile(fixture('menu_preprocessor.ejs'), {filename: file}); var fn = ejs.compile(fixture('menu_preprocessor.ejs'), {filename: file});
assert(fn.dependencies.length); assert(fn.dependencies.length);
}); });
@ -842,12 +911,12 @@ suite('preprocessor include', function () {
}); });
test('pass compileDebug to include', function () { test('pass compileDebug to include', function () {
var file = 'test/fixtures/include_preprocessor.ejs' var file = 'test/fixtures/include_preprocessor.ejs';
, fn; var fn;
fn = ejs.compile(fixture('include_preprocessor.ejs'), { fn = ejs.compile(fixture('include_preprocessor.ejs'), {
filename: file filename: file,
, delimiter: '@' delimiter: '@',
, compileDebug: false compileDebug: false
}); });
try { try {
// Render without a required variable reference // Render without a required variable reference
@ -863,9 +932,9 @@ suite('preprocessor include', function () {
test('is static', function () { test('is static', function () {
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>'); fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_preprocessor_cache.ejs' var file = 'test/fixtures/include_preprocessor_cache.ejs';
, options = {filename: file} var options = {filename: file};
, out = ejs.compile(fixture('include_preprocessor_cache.ejs'), options); var out = ejs.compile(fixture('include_preprocessor_cache.ejs'), options);
assert.equal(out(), '<p>Old</p>\n'); assert.equal(out(), '<p>Old</p>\n');
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>'); fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>');
@ -874,16 +943,25 @@ suite('preprocessor include', function () {
test('support caching', function () { test('support caching', function () {
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>'); fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_preprocessor_cache.ejs' var file = 'test/fixtures/include_preprocessor_cache.ejs';
, options = {cache: true, filename: file} var options = {cache: true, filename: file};
, out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options) var out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options);
, expected = fixture('include_preprocessor_cache.html'); var expected = fixture('include_preprocessor_cache.html');
assert.equal(out, expected); assert.equal(out, expected);
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>'); fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>');
out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options); out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options);
assert.equal(out, expected); assert.equal(out, expected);
}); });
test('whitespace slurp and rmWhitespace work', function() {
var file = 'test/fixtures/include_preprocessor_line_slurp.ejs';
var template = fixture('include_preprocessor_line_slurp.ejs');
var expected = fixture('include_preprocessor_line_slurp.html');
var options = {rmWhitespace: true, filename: file};
assert.equal(ejs.render(template, {}, options),
expected);
});
}); });
suite('comments', function () { suite('comments', function () {
@ -897,12 +975,31 @@ suite('require', function () {
// Only works with inline/preprocessor includes // Only works with inline/preprocessor includes
test('allow ejs templates to be required as node modules', function () { test('allow ejs templates to be required as node modules', function () {
var file = 'test/fixtures/include_preprocessor.ejs' var file = 'test/fixtures/include_preprocessor.ejs';
, template = require(__dirname + '/fixtures/menu_preprocessor.ejs'); var template = require(__dirname + '/fixtures/menu_preprocessor.ejs');
if (!process.env.running_under_istanbul) { if (!process.env.running_under_istanbul) {
assert.equal(template({filename: file, pets: users}), assert.equal(template({filename: file, pets: users}),
fixture('menu_preprocessor.html')); fixture('menu_preprocessor.html'));
}
});
});
suite('test fileloader', function () {
var myFileLoad = function (filePath) {
return 'myFileLoad: ' + fs.readFileSync(filePath);
};
test('test custom fileload', function (done) {
ejs.fileLoader = myFileLoad;
ejs.renderFile('test/fixtures/para.ejs', function(err, html) {
if (err) {
return done(err);
} }
assert.equal(html, 'myFileLoad: <p>hey</p>\n');
done();
});
}); });
}); });
@ -914,8 +1011,8 @@ suite('examples', function () {
} }
suite(f, function () { suite(f, function () {
test('doesn\'t throw any errors', function () { test('doesn\'t throw any errors', function () {
var stderr = hook_stdio(process.stderr, noop) var stderr = hook_stdio(process.stderr, noop);
, stdout = hook_stdio(process.stdout, noop); var stdout = hook_stdio(process.stdout, noop);
try { try {
require('../examples/' + f); require('../examples/' + f);
} }
@ -930,3 +1027,13 @@ suite('examples', function () {
}); });
}); });
}); });
suite('meta information', function () {
test('has a version', function () {
assert.strictEqual(ejs.VERSION, require('../package.json').version);
});
test('had a name', function () {
assert.strictEqual(ejs.name, 'ejs');
});
});

View File

@ -1,5 +1,5 @@
AAA AAA
<% data = "test"; -%> <% var data = "test"; -%>
BBB BBB
<%= qdata %> <%= qdata %>
CCC CCC

208
node_modules/express/History.md generated vendored
View File

@ -1,3 +1,211 @@
4.15.3 / 2017-05-16
===================
* Fix error when `res.set` cannot add charset to `Content-Type`
* deps: debug@2.6.7
- Fix `DEBUG_MAX_ARRAY_LENGTH`
- deps: ms@2.0.0
* deps: finalhandler@~1.0.3
- Fix missing `</html>` in HTML document
- deps: debug@2.6.7
* deps: proxy-addr@~1.1.4
- deps: ipaddr.js@1.3.0
* deps: send@0.15.3
- deps: debug@2.6.7
- deps: ms@2.0.0
* deps: serve-static@1.12.3
- deps: send@0.15.3
* deps: type-is@~1.6.15
- deps: mime-types@~2.1.15
* deps: vary@~1.1.1
- perf: hoist regular expression
4.15.2 / 2017-03-06
===================
* deps: qs@6.4.0
- Fix regression parsing keys starting with `[`
4.15.1 / 2017-03-05
===================
* deps: send@0.15.1
- Fix issue when `Date.parse` does not return `NaN` on invalid date
- Fix strict violation in broken environments
* deps: serve-static@1.12.1
- Fix issue when `Date.parse` does not return `NaN` on invalid date
- deps: send@0.15.1
4.15.0 / 2017-03-01
===================
* Add debug message when loading view engine
* Add `next("router")` to exit from router
* Fix case where `router.use` skipped requests routes did not
* Remove usage of `res._headers` private field
- Improves compatibility with Node.js 8 nightly
* Skip routing when `req.url` is not set
* Use `%o` in path debug to tell types apart
* Use `Object.create` to setup request & response prototypes
* Use `setprototypeof` module to replace `__proto__` setting
* Use `statuses` instead of `http` module for status messages
* deps: debug@2.6.1
- Allow colors in workers
- Deprecated `DEBUG_FD` environment variable set to `3` or higher
- Fix error when running under React Native
- Use same color for same namespace
- deps: ms@0.7.2
* deps: etag@~1.8.0
- Use SHA1 instead of MD5 for ETag hashing
- Works with FIPS 140-2 OpenSSL configuration
* deps: finalhandler@~1.0.0
- Fix exception when `err` cannot be converted to a string
- Fully URL-encode the pathname in the 404
- Only include the pathname in the 404 message
- Send complete HTML document
- Set `Content-Security-Policy: default-src 'self'` header
- deps: debug@2.6.1
* deps: fresh@0.5.0
- Fix false detection of `no-cache` request directive
- Fix incorrect result when `If-None-Match` has both `*` and ETags
- Fix weak `ETag` matching to match spec
- perf: delay reading header values until needed
- perf: enable strict mode
- perf: hoist regular expressions
- perf: remove duplicate conditional
- perf: remove unnecessary boolean coercions
- perf: skip checking modified time if ETag check failed
- perf: skip parsing `If-None-Match` when no `ETag` header
- perf: use `Date.parse` instead of `new Date`
* deps: qs@6.3.1
- Fix array parsing from skipping empty values
- Fix compacting nested arrays
* deps: send@0.15.0
- Fix false detection of `no-cache` request directive
- Fix incorrect result when `If-None-Match` has both `*` and ETags
- Fix weak `ETag` matching to match spec
- Remove usage of `res._headers` private field
- Support `If-Match` and `If-Unmodified-Since` headers
- Use `res.getHeaderNames()` when available
- Use `res.headersSent` when available
- deps: debug@2.6.1
- deps: etag@~1.8.0
- deps: fresh@0.5.0
- deps: http-errors@~1.6.1
* deps: serve-static@1.12.0
- Fix false detection of `no-cache` request directive
- Fix incorrect result when `If-None-Match` has both `*` and ETags
- Fix weak `ETag` matching to match spec
- Remove usage of `res._headers` private field
- Send complete HTML document in redirect response
- Set default CSP header in redirect response
- Support `If-Match` and `If-Unmodified-Since` headers
- Use `res.getHeaderNames()` when available
- Use `res.headersSent` when available
- deps: send@0.15.0
* perf: add fast match path for `*` route
* perf: improve `req.ips` performance
4.14.1 / 2017-01-28
===================
* deps: content-disposition@0.5.2
* deps: finalhandler@0.5.1
- Fix exception when `err.headers` is not an object
- deps: statuses@~1.3.1
- perf: hoist regular expressions
- perf: remove duplicate validation path
* deps: proxy-addr@~1.1.3
- deps: ipaddr.js@1.2.0
* deps: send@0.14.2
- deps: http-errors@~1.5.1
- deps: ms@0.7.2
- deps: statuses@~1.3.1
* deps: serve-static@~1.11.2
- deps: send@0.14.2
* deps: type-is@~1.6.14
- deps: mime-types@~2.1.13
4.14.0 / 2016-06-16
===================
* Add `acceptRanges` option to `res.sendFile`/`res.sendfile`
* Add `cacheControl` option to `res.sendFile`/`res.sendfile`
* Add `options` argument to `req.range`
- Includes the `combine` option
* Encode URL in `res.location`/`res.redirect` if not already encoded
* Fix some redirect handling in `res.sendFile`/`res.sendfile`
* Fix Windows absolute path check using forward slashes
* Improve error with invalid arguments to `req.get()`
* Improve performance for `res.json`/`res.jsonp` in most cases
* Improve `Range` header handling in `res.sendFile`/`res.sendfile`
* deps: accepts@~1.3.3
- Fix including type extensions in parameters in `Accept` parsing
- Fix parsing `Accept` parameters with quoted equals
- Fix parsing `Accept` parameters with quoted semicolons
- Many performance improvments
- deps: mime-types@~2.1.11
- deps: negotiator@0.6.1
* deps: content-type@~1.0.2
- perf: enable strict mode
* deps: cookie@0.3.1
- Add `sameSite` option
- Fix cookie `Max-Age` to never be a floating point number
- Improve error message when `encode` is not a function
- Improve error message when `expires` is not a `Date`
- Throw better error for invalid argument to parse
- Throw on invalid values provided to `serialize`
- perf: enable strict mode
- perf: hoist regular expression
- perf: use for loop in parse
- perf: use string concatination for serialization
* deps: finalhandler@0.5.0
- Change invalid or non-numeric status code to 500
- Overwrite status message to match set status code
- Prefer `err.statusCode` if `err.status` is invalid
- Set response headers from `err.headers` object
- Use `statuses` instead of `http` module for status messages
* deps: proxy-addr@~1.1.2
- Fix accepting various invalid netmasks
- Fix IPv6-mapped IPv4 validation edge cases
- IPv4 netmasks must be contingous
- IPv6 addresses cannot be used as a netmask
- deps: ipaddr.js@1.1.1
* deps: qs@6.2.0
- Add `decoder` option in `parse` function
* deps: range-parser@~1.2.0
- Add `combine` option to combine overlapping ranges
- Fix incorrectly returning -1 when there is at least one valid range
- perf: remove internal function
* deps: send@0.14.1
- Add `acceptRanges` option
- Add `cacheControl` option
- Attempt to combine multiple ranges into single range
- Correctly inherit from `Stream` class
- Fix `Content-Range` header in 416 responses when using `start`/`end` options
- Fix `Content-Range` header missing from default 416 responses
- Fix redirect error when `path` contains raw non-URL characters
- Fix redirect when `path` starts with multiple forward slashes
- Ignore non-byte `Range` headers
- deps: http-errors@~1.5.0
- deps: range-parser@~1.2.0
- deps: statuses@~1.3.0
- perf: remove argument reassignment
* deps: serve-static@~1.11.1
- Add `acceptRanges` option
- Add `cacheControl` option
- Attempt to combine multiple ranges into single range
- Fix redirect error when `req.url` contains raw non-URL characters
- Ignore non-byte `Range` headers
- Use status code 301 for redirects
- deps: send@0.14.1
* deps: type-is@~1.6.13
- Fix type error when given invalid type to match against
- deps: mime-types@~2.1.11
* deps: vary@~1.1.0
- Only accept valid field names in the `field` argument
* perf: use strict equality when possible
4.13.4 / 2016-01-21 4.13.4 / 2016-01-21
=================== ===================

7
node_modules/express/Readme.md generated vendored
View File

@ -37,13 +37,16 @@ $ npm install express
## Docs & Community ## Docs & Community
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
* [Github Organization](https://github.com/expressjs) for Official Middleware & Modules * [Github Organization](https://github.com/expressjs) for Official Middleware & Modules
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
* [Google Group](https://groups.google.com/group/express-js) for discussion * [Google Group](https://groups.google.com/group/express-js) for discussion
* [Gitter](https://gitter.im/expressjs/express) for support and discussion * [Gitter](https://gitter.im/expressjs/express) for support and discussion
* [Русскоязычная документация](http://jsman.ru/express/)
###Security Issues **PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
### Security Issues
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md). If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).

View File

@ -28,6 +28,7 @@ var deprecate = require('depd')('express');
var flatten = require('array-flatten'); var flatten = require('array-flatten');
var merge = require('utils-merge'); var merge = require('utils-merge');
var resolve = require('path').resolve; var resolve = require('path').resolve;
var setPrototypeOf = require('setprototypeof')
var slice = Array.prototype.slice; var slice = Array.prototype.slice;
/** /**
@ -94,10 +95,10 @@ app.defaultConfiguration = function defaultConfiguration() {
} }
// inherit protos // inherit protos
this.request.__proto__ = parent.request; setPrototypeOf(this.request, parent.request)
this.response.__proto__ = parent.response; setPrototypeOf(this.response, parent.response)
this.engines.__proto__ = parent.engines; setPrototypeOf(this.engines, parent.engines)
this.settings.__proto__ = parent.settings; setPrototypeOf(this.settings, parent.settings)
}); });
// setup locals // setup locals
@ -227,8 +228,8 @@ app.use = function use(fn) {
router.use(path, function mounted_app(req, res, next) { router.use(path, function mounted_app(req, res, next) {
var orig = req.app; var orig = req.app;
fn.handle(req, res, function (err) { fn.handle(req, res, function (err) {
req.__proto__ = orig.request; setPrototypeOf(req, orig.request)
res.__proto__ = orig.response; setPrototypeOf(res, orig.response)
next(err); next(err);
}); });
}); });
@ -261,9 +262,9 @@ app.route = function route(path) {
* *
* By default will `require()` the engine based on the * By default will `require()` the engine based on the
* file extension. For example if you try to render * file extension. For example if you try to render
* a "foo.jade" file Express will invoke the following internally: * a "foo.ejs" file Express will invoke the following internally:
* *
* app.engine('jade', require('jade').__express); * app.engine('ejs', require('ejs').__express);
* *
* For engines that do not provide `.__express` out of the box, * For engines that do not provide `.__express` out of the box,
* or if you wish to "map" a different extension to the template engine * or if you wish to "map" a different extension to the template engine

12
node_modules/express/lib/express.js generated vendored
View File

@ -41,8 +41,16 @@ function createApplication() {
mixin(app, EventEmitter.prototype, false); mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false); mixin(app, proto, false);
app.request = { __proto__: req, app: app }; // expose the prototype that will get set on requests
app.response = { __proto__: res, app: app }; app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// expose the prototype that will get set on responses
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
app.init(); app.init();
return app; return app;
} }

78
node_modules/express/lib/request.js generated vendored
View File

@ -25,11 +25,17 @@ var proxyaddr = require('proxy-addr');
/** /**
* Request prototype. * Request prototype.
* @public
*/ */
var req = exports = module.exports = { var req = Object.create(http.IncomingMessage.prototype)
__proto__: http.IncomingMessage.prototype
}; /**
* Module exports.
* @public
*/
module.exports = req
/** /**
* Return request header. * Return request header.
@ -57,6 +63,14 @@ var req = exports = module.exports = {
req.get = req.get =
req.header = function header(name) { req.header = function header(name) {
if (!name) {
throw new TypeError('name argument is required to req.get');
}
if (typeof name !== 'string') {
throw new TypeError('name must be a string to req.get');
}
var lc = name.toLowerCase(); var lc = name.toLowerCase();
switch (lc) { switch (lc) {
@ -171,29 +185,34 @@ req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
'req.acceptsLanguage: Use acceptsLanguages instead'); 'req.acceptsLanguage: Use acceptsLanguages instead');
/** /**
* Parse Range header field, * Parse Range header field, capping to the given `size`.
* capping to the given `size`.
* *
* Unspecified ranges such as "0-" require * Unspecified ranges such as "0-" require knowledge of your resource length. In
* knowledge of your resource length. In * the case of a byte range this is of course the total number of bytes. If the
* the case of a byte range this is of course * Range header field is not given `undefined` is returned, `-1` when unsatisfiable,
* the total number of bytes. If the Range * and `-2` when syntactically invalid.
* header field is not given `null` is returned,
* `-1` when unsatisfiable, `-2` when syntactically invalid.
* *
* NOTE: remember that ranges are inclusive, so * When ranges are returned, the array has a "type" property which is the type of
* for example "Range: users=0-3" should respond * range that is required (most commonly, "bytes"). Each array element is an object
* with 4 users when available, not 3. * with a "start" and "end" property for the portion of the range.
* *
* @param {Number} size * The "combine" option can be set to `true` and overlapping & adjacent ranges
* @return {Array} * will be combined into a single range.
*
* NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"
* should respond with 4 users when available, not 3.
*
* @param {number} size
* @param {object} [options]
* @param {boolean} [options.combine=false]
* @return {number|array}
* @public * @public
*/ */
req.range = function(size){ req.range = function range(size, options) {
var range = this.get('Range'); var range = this.get('Range');
if (!range) return; if (!range) return;
return parseRange(size, range); return parseRange(size, range, options);
}; };
/** /**
@ -303,7 +322,7 @@ defineGetter(req, 'protocol', function protocol(){
/** /**
* Short-hand for: * Short-hand for:
* *
* req.protocol == 'https' * req.protocol === 'https'
* *
* @return {Boolean} * @return {Boolean}
* @public * @public
@ -343,7 +362,12 @@ defineGetter(req, 'ip', function ip(){
defineGetter(req, 'ips', function ips() { defineGetter(req, 'ips', function ips() {
var trust = this.app.get('trust proxy fn'); var trust = this.app.get('trust proxy fn');
var addrs = proxyaddr.all(this, trust); var addrs = proxyaddr.all(this, trust);
return addrs.slice(1).reverse();
// reverse the order (to farthest -> closest)
// and remove socket address
addrs.reverse().pop()
return addrs
}); });
/** /**
@ -434,14 +458,18 @@ defineGetter(req, 'host', deprecate.function(function host(){
defineGetter(req, 'fresh', function(){ defineGetter(req, 'fresh', function(){
var method = this.method; var method = this.method;
var s = this.res.statusCode; var res = this.res
var status = res.statusCode
// GET or HEAD for weak freshness validation only // GET or HEAD for weak freshness validation only
if ('GET' != method && 'HEAD' != method) return false; if ('GET' !== method && 'HEAD' !== method) return false;
// 2xx or 304 as per rfc2616 14.26 // 2xx or 304 as per rfc2616 14.26
if ((s >= 200 && s < 300) || 304 == s) { if ((status >= 200 && status < 300) || 304 === status) {
return fresh(this.headers, (this.res._headers || {})); return fresh(this.headers, {
'etag': res.get('ETag'),
'last-modified': res.get('Last-Modified')
})
} }
return false; return false;
@ -486,4 +514,4 @@ function defineGetter(obj, name, getter) {
enumerable: true, enumerable: true,
get: getter get: getter
}); });
}; }

63
node_modules/express/lib/response.js generated vendored
View File

@ -14,17 +14,18 @@
var contentDisposition = require('content-disposition'); var contentDisposition = require('content-disposition');
var deprecate = require('depd')('express'); var deprecate = require('depd')('express');
var encodeUrl = require('encodeurl');
var escapeHtml = require('escape-html'); var escapeHtml = require('escape-html');
var http = require('http'); var http = require('http');
var isAbsolute = require('./utils').isAbsolute; var isAbsolute = require('./utils').isAbsolute;
var onFinished = require('on-finished'); var onFinished = require('on-finished');
var path = require('path'); var path = require('path');
var statuses = require('statuses')
var merge = require('utils-merge'); var merge = require('utils-merge');
var sign = require('cookie-signature').sign; var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType; var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes; var normalizeTypes = require('./utils').normalizeTypes;
var setCharset = require('./utils').setCharset; var setCharset = require('./utils').setCharset;
var statusCodes = http.STATUS_CODES;
var cookie = require('cookie'); var cookie = require('cookie');
var send = require('send'); var send = require('send');
var extname = path.extname; var extname = path.extname;
@ -34,11 +35,17 @@ var vary = require('vary');
/** /**
* Response prototype. * Response prototype.
* @public
*/ */
var res = module.exports = { var res = Object.create(http.ServerResponse.prototype)
__proto__: http.ServerResponse.prototype
}; /**
* Module exports.
* @public
*/
module.exports = res
/** /**
* Module variables. * Module variables.
@ -128,7 +135,7 @@ res.send = function send(body) {
deprecate('res.send(status): Use res.sendStatus(status) instead'); deprecate('res.send(status): Use res.sendStatus(status) instead');
this.statusCode = chunk; this.statusCode = chunk;
chunk = statusCodes[chunk]; chunk = statuses[chunk]
} }
switch (typeof chunk) { switch (typeof chunk) {
@ -189,7 +196,7 @@ res.send = function send(body) {
if (req.fresh) this.statusCode = 304; if (req.fresh) this.statusCode = 304;
// strip irrelevant headers // strip irrelevant headers
if (204 == this.statusCode || 304 == this.statusCode) { if (204 === this.statusCode || 304 === this.statusCode) {
this.removeHeader('Content-Type'); this.removeHeader('Content-Type');
this.removeHeader('Content-Length'); this.removeHeader('Content-Length');
this.removeHeader('Transfer-Encoding'); this.removeHeader('Transfer-Encoding');
@ -239,7 +246,7 @@ res.json = function json(obj) {
var app = this.app; var app = this.app;
var replacer = app.get('json replacer'); var replacer = app.get('json replacer');
var spaces = app.get('json spaces'); var spaces = app.get('json spaces');
var body = JSON.stringify(val, replacer, spaces); var body = stringify(val, replacer, spaces);
// content-type // content-type
if (!this.get('Content-Type')) { if (!this.get('Content-Type')) {
@ -281,7 +288,7 @@ res.jsonp = function jsonp(obj) {
var app = this.app; var app = this.app;
var replacer = app.get('json replacer'); var replacer = app.get('json replacer');
var spaces = app.get('json spaces'); var spaces = app.get('json spaces');
var body = JSON.stringify(val, replacer, spaces); var body = stringify(val, replacer, spaces);
var callback = this.req.query[app.get('jsonp callback name')]; var callback = this.req.query[app.get('jsonp callback name')];
// content-type // content-type
@ -333,7 +340,7 @@ res.jsonp = function jsonp(obj) {
*/ */
res.sendStatus = function sendStatus(statusCode) { res.sendStatus = function sendStatus(statusCode) {
var body = statusCodes[statusCode] || String(statusCode); var body = statuses[statusCode] || String(statusCode)
this.statusCode = statusCode; this.statusCode = statusCode;
this.type('txt'); this.type('txt');
@ -710,9 +717,14 @@ res.header = function header(field, val) {
: String(val); : String(val);
// add charset to content-type // add charset to content-type
if (field.toLowerCase() === 'content-type' && !charsetRegExp.test(value)) { if (field.toLowerCase() === 'content-type') {
var charset = mime.charsets.lookup(value.split(';')[0]); if (Array.isArray(value)) {
if (charset) value += '; charset=' + charset.toLowerCase(); throw new TypeError('Content-Type cannot be set to an Array');
}
if (!charsetRegExp.test(value)) {
var charset = mime.charsets.lookup(value.split(';')[0]);
if (charset) value += '; charset=' + charset.toLowerCase();
}
} }
this.setHeader(field, value); this.setHeader(field, value);
@ -740,7 +752,7 @@ res.get = function(field){
* Clear cookie `name`. * Clear cookie `name`.
* *
* @param {String} name * @param {String} name
* @param {Object} options * @param {Object} [options]
* @return {ServerResponse} for chaining * @return {ServerResponse} for chaining
* @public * @public
*/ */
@ -770,7 +782,7 @@ res.clearCookie = function clearCookie(name, options) {
* *
* @param {String} name * @param {String} name
* @param {String|Object} value * @param {String|Object} value
* @param {Options} options * @param {Object} [options]
* @return {ServerResponse} for chaining * @return {ServerResponse} for chaining
* @public * @public
*/ */
@ -832,8 +844,7 @@ res.location = function location(url) {
} }
// set location // set location
this.set('Location', loc); return this.set('Location', encodeUrl(loc));
return this;
}; };
/** /**
@ -871,18 +882,17 @@ res.redirect = function redirect(url) {
} }
// Set location header // Set location header
this.location(address); address = this.location(address).get('Location');
address = this.get('Location');
// Support text/{plain,html} by default // Support text/{plain,html} by default
this.format({ this.format({
text: function(){ text: function(){
body = statusCodes[status] + '. Redirecting to ' + encodeURI(address); body = statuses[status] + '. Redirecting to ' + address
}, },
html: function(){ html: function(){
var u = escapeHtml(address); var u = escapeHtml(address);
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'; body = '<p>' + statuses[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
}, },
default: function(){ default: function(){
@ -1051,3 +1061,16 @@ function sendfile(res, file, options, callback) {
// pipe // pipe
file.pipe(res); file.pipe(res);
} }
/**
* Stringify JSON, like JSON.stringify, but v8 optimized.
* @private
*/
function stringify(value, replacer, spaces) {
// v8 checks arguments.length for optimizing simple call
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
return replacer || spaces
? JSON.stringify(value, replacer, spaces)
: JSON.stringify(value);
}

View File

@ -21,6 +21,7 @@ var debug = require('debug')('express:router');
var deprecate = require('depd')('express'); var deprecate = require('depd')('express');
var flatten = require('array-flatten'); var flatten = require('array-flatten');
var parseUrl = require('parseurl'); var parseUrl = require('parseurl');
var setPrototypeOf = require('setprototypeof')
/** /**
* Module variables. * Module variables.
@ -47,7 +48,7 @@ var proto = module.exports = function(options) {
} }
// mixin Router class functions // mixin Router class functions
router.__proto__ = proto; setPrototypeOf(router, proto)
router.params = {}; router.params = {};
router._params = []; router._params = [];
@ -119,7 +120,7 @@ proto.param = function param(name, fn) {
// ensure we end up with a // ensure we end up with a
// middleware function // middleware function
if ('function' != typeof fn) { if ('function' !== typeof fn) {
throw new Error('invalid param() call for ' + name + ', got ' + fn); throw new Error('invalid param() call for ' + name + ', got ' + fn);
} }
@ -137,11 +138,8 @@ proto.handle = function handle(req, res, out) {
debug('dispatching %s %s', req.method, req.url); debug('dispatching %s %s', req.method, req.url);
var search = 1 + req.url.indexOf('?');
var pathlength = search ? search - 1 : req.url.length;
var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://');
var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
var idx = 0; var idx = 0;
var protohost = getProtohost(req.url) || ''
var removed = ''; var removed = '';
var slashAdded = false; var slashAdded = false;
var paramcalled = {}; var paramcalled = {};
@ -193,6 +191,12 @@ proto.handle = function handle(req, res, out) {
removed = ''; removed = '';
} }
// signal to exit router
if (layerError === 'router') {
setImmediate(done, null)
return
}
// no more matching layers // no more matching layers
if (idx >= stack.length) { if (idx >= stack.length) {
setImmediate(done, layerError); setImmediate(done, layerError);
@ -282,18 +286,19 @@ proto.handle = function handle(req, res, out) {
} }
function trim_prefix(layer, layerError, layerPath, path) { function trim_prefix(layer, layerError, layerPath, path) {
var c = path[layerPath.length];
if (c && '/' !== c && '.' !== c) return next(layerError);
// Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped
if (layerPath.length !== 0) { if (layerPath.length !== 0) {
// Validate path breaks on a path separator
var c = path[layerPath.length]
if (c && c !== '/' && c !== '.') return next(layerError)
// Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped
debug('trim prefix (%s) from url %s', layerPath, req.url); debug('trim prefix (%s) from url %s', layerPath, req.url);
removed = layerPath; removed = layerPath;
req.url = protohost + req.url.substr(protohost.length + removed.length); req.url = protohost + req.url.substr(protohost.length + removed.length);
// Ensure leading slash // Ensure leading slash
if (!fqdn && req.url[0] !== '/') { if (!protohost && req.url[0] !== '/') {
req.url = '/' + req.url; req.url = '/' + req.url;
slashAdded = true; slashAdded = true;
} }
@ -351,11 +356,6 @@ proto.process_params = function process_params(layer, called, req, res, done) {
paramIndex = 0; paramIndex = 0;
key = keys[i++]; key = keys[i++];
if (!key) {
return done();
}
name = key.name; name = key.name;
paramVal = req.params[name]; paramVal = req.params[name];
paramCallbacks = params[name]; paramCallbacks = params[name];
@ -459,7 +459,7 @@ proto.use = function use(fn) {
} }
// add the middleware // add the middleware
debug('use %s %s', path, fn.name || '<anonymous>'); debug('use %o %s', path, fn.name || '<anonymous>')
var layer = new Layer(path, { var layer = new Layer(path, {
sensitive: this.caseSensitive, sensitive: this.caseSensitive,
@ -531,6 +531,23 @@ function getPathname(req) {
} }
} }
// Get get protocol + host for a URL
function getProtohost(url) {
if (typeof url !== 'string' || url.length === 0 || url[0] === '/') {
return undefined
}
var searchIndex = url.indexOf('?')
var pathLength = searchIndex !== -1
? searchIndex
: url.length
var fqdnIndex = url.substr(0, pathLength).indexOf('://')
return fqdnIndex !== -1
? url.substr(0, url.indexOf('/', 3 + fqdnIndex))
: undefined
}
// get type for error message // get type for error message
function gettype(obj) { function gettype(obj) {
var type = typeof obj; var type = typeof obj;
@ -609,7 +626,7 @@ function restore(fn, obj) {
vals[i] = obj[props[i]]; vals[i] = obj[props[i]];
} }
return function(err){ return function () {
// restore vals // restore vals
for (var i = 0; i < props.length; i++) { for (var i = 0; i < props.length; i++) {
obj[props[i]] = vals[i]; obj[props[i]] = vals[i];

View File

@ -44,7 +44,7 @@ function Route(path) {
this.path = path; this.path = path;
this.stack = []; this.stack = [];
debug('new %s', path); debug('new %o', path)
// route handlers for various http methods // route handlers for various http methods
this.methods = {}; this.methods = {};
@ -112,10 +112,16 @@ Route.prototype.dispatch = function dispatch(req, res, done) {
next(); next();
function next(err) { function next(err) {
// signal to exit route
if (err && err === 'route') { if (err && err === 'route') {
return done(); return done();
} }
// signal to exit router
if (err && err === 'router') {
return done(err)
}
var layer = stack[idx++]; var layer = stack[idx++];
if (!layer) { if (!layer) {
return done(err); return done(err);
@ -196,7 +202,7 @@ methods.forEach(function(method){
throw new Error(msg); throw new Error(msg);
} }
debug('%s %s', method, this.path); debug('%s %o', method, this.path)
var layer = Layer('/', {}, handle); var layer = Layer('/', {}, handle);
layer.method = method; layer.method = method;

9
node_modules/express/lib/utils.js generated vendored
View File

@ -66,9 +66,9 @@ exports.wetag = function wetag(body, encoding){
*/ */
exports.isAbsolute = function(path){ exports.isAbsolute = function(path){
if ('/' == path[0]) return true; if ('/' === path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true; if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path
}; };
/** /**
@ -142,7 +142,7 @@ function acceptParams(str, index) {
for (var i = 1; i < parts.length; ++i) { for (var i = 1; i < parts.length; ++i) {
var pms = parts[i].split(/ *= */); var pms = parts[i].split(/ *= */);
if ('q' == pms[0]) { if ('q' === pms[0]) {
ret.quality = parseFloat(pms[1]); ret.quality = parseFloat(pms[1]);
} else { } else {
ret.params[pms[0]] = pms[1]; ret.params[pms[0]] = pms[1];
@ -283,7 +283,6 @@ exports.setCharset = function setCharset(type, charset) {
function parseExtendedQueryString(str) { function parseExtendedQueryString(str) {
return qs.parse(str, { return qs.parse(str, {
allowDots: false,
allowPrototypes: true allowPrototypes: true
}); });
} }

4
node_modules/express/lib/view.js generated vendored
View File

@ -75,7 +75,9 @@ function View(name, options) {
if (!opts.engines[this.ext]) { if (!opts.engines[this.ext]) {
// load engine // load engine
opts.engines[this.ext] = require(this.ext.substr(1)).__express; var mod = this.ext.substr(1)
debug('require "%s"', mod)
opts.engines[this.ext] = require(mod).__express
} }
// store loaded engine // store loaded engine

180
node_modules/express/package.json generated vendored
View File

@ -1,39 +1,63 @@
{ {
"_args": [ "_args": [
[ [
"express@^4.13.4", {
"/Users/martin/newdev/Rinser" "name": "express",
"raw": "express@4.15.3",
"rawSpec": "4.15.3",
"scope": null,
"spec": "4.15.3",
"type": "version"
},
"/home/martind2000/dev/Rinser"
] ]
], ],
"_from": "express@>=4.13.4 <5.0.0", "_from": "express@4.15.3",
"_id": "express@4.13.4", "_id": "express@4.15.3",
"_inCache": true, "_inCache": true,
"_installable": true, "_installable": true,
"_location": "/express", "_location": "/express",
"_nodeVersion": "6.10.3",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/express-4.15.3.tgz_1495030658380_0.1599606357049197"
},
"_npmUser": { "_npmUser": {
"email": "doug@somethingdoug.com", "email": "doug@somethingdoug.com",
"name": "dougwilson" "name": "dougwilson"
}, },
"_npmVersion": "1.4.28", "_npmVersion": "4.2.0",
"_phantomChildren": { "_phantomChildren": {
"mime-types": "2.1.11" "depd": "1.1.0",
"destroy": "1.0.4",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"etag": "1.8.0",
"forwarded": "0.1.0",
"fresh": "0.5.0",
"media-typer": "0.3.0",
"mime": "1.3.4",
"on-finished": "2.3.0",
"parseurl": "1.3.1",
"range-parser": "1.2.0",
"unpipe": "1.0.0"
}, },
"_requested": { "_requested": {
"name": "express", "name": "express",
"raw": "express@^4.13.4", "raw": "express@4.15.3",
"rawSpec": "^4.13.4", "rawSpec": "4.15.3",
"scope": null, "scope": null,
"spec": ">=4.13.4 <5.0.0", "spec": "4.15.3",
"type": "range" "type": "version"
}, },
"_requiredBy": [ "_requiredBy": [
"/" "/"
], ],
"_resolved": "http://registry.npmjs.org/express/-/express-4.13.4.tgz", "_resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz",
"_shasum": "3c0b76f3c77590c8345739061ec0bd3ba067ec24", "_shasum": "bab65d0f03aa80c358408972fc700f916944b662",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "express@^4.13.4", "_spec": "express@4.15.3",
"_where": "/Users/martin/newdev/Rinser", "_where": "/home/martind2000/dev/Rinser",
"author": { "author": {
"email": "tj@vision-media.ca", "email": "tj@vision-media.ca",
"name": "TJ Holowaychuk" "name": "TJ Holowaychuk"
@ -43,114 +67,130 @@
}, },
"contributors": [ "contributors": [
{ {
"name": "Aaron Heckmann", "email": "aaron.heckmann+github@gmail.com",
"email": "aaron.heckmann+github@gmail.com" "name": "Aaron Heckmann"
}, },
{ {
"name": "Ciaran Jessup", "email": "ciaranj@gmail.com",
"email": "ciaranj@gmail.com" "name": "Ciaran Jessup"
}, },
{ {
"name": "Douglas Christopher Wilson", "email": "doug@somethingdoug.com",
"email": "doug@somethingdoug.com" "name": "Douglas Christopher Wilson"
}, },
{ {
"name": "Guillermo Rauch", "email": "rauchg@gmail.com",
"email": "rauchg@gmail.com" "name": "Guillermo Rauch"
}, },
{ {
"name": "Jonathan Ong", "email": "me@jongleberry.com",
"email": "me@jongleberry.com" "name": "Jonathan Ong"
}, },
{ {
"name": "Roman Shtylman", "email": "shtylman+expressjs@gmail.com",
"email": "shtylman+expressjs@gmail.com" "name": "Roman Shtylman"
}, },
{ {
"name": "Young Jae Sim", "email": "hanul@hanul.me",
"email": "hanul@hanul.me" "name": "Young Jae Sim"
} }
], ],
"dependencies": { "dependencies": {
"accepts": "~1.2.12", "accepts": "~1.3.3",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
"content-disposition": "0.5.1", "content-disposition": "0.5.2",
"content-type": "~1.0.1", "content-type": "~1.0.2",
"cookie": "0.1.5", "cookie": "0.3.1",
"cookie-signature": "1.0.6", "cookie-signature": "1.0.6",
"debug": "~2.2.0", "debug": "2.6.7",
"depd": "~1.1.0", "depd": "~1.1.0",
"encodeurl": "~1.0.1",
"escape-html": "~1.0.3", "escape-html": "~1.0.3",
"etag": "~1.7.0", "etag": "~1.8.0",
"finalhandler": "0.4.1", "finalhandler": "~1.0.3",
"fresh": "0.3.0", "fresh": "0.5.0",
"merge-descriptors": "1.0.1", "merge-descriptors": "1.0.1",
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "~2.3.0", "on-finished": "~2.3.0",
"parseurl": "~1.3.1", "parseurl": "~1.3.1",
"path-to-regexp": "0.1.7", "path-to-regexp": "0.1.7",
"proxy-addr": "~1.0.10", "proxy-addr": "~1.1.4",
"qs": "4.0.0", "qs": "6.4.0",
"range-parser": "~1.0.3", "range-parser": "~1.2.0",
"send": "0.13.1", "send": "0.15.3",
"serve-static": "~1.10.2", "serve-static": "1.12.3",
"type-is": "~1.6.6", "setprototypeof": "1.0.3",
"statuses": "~1.3.1",
"type-is": "~1.6.15",
"utils-merge": "1.0.0", "utils-merge": "1.0.0",
"vary": "~1.0.1" "vary": "~1.1.1"
}, },
"description": "Fast, unopinionated, minimalist web framework", "description": "Fast, unopinionated, minimalist web framework",
"devDependencies": { "devDependencies": {
"after": "0.8.1", "after": "0.8.2",
"body-parser": "~1.14.2", "body-parser": "1.17.1",
"connect-redis": "~2.4.1", "connect-redis": "~2.4.1",
"cookie-parser": "~1.4.1", "cookie-parser": "~1.4.3",
"cookie-session": "~1.2.0", "cookie-session": "~1.2.0",
"ejs": "2.3.4", "ejs": "2.5.6",
"express-session": "~1.13.0", "express-session": "1.15.2",
"istanbul": "0.4.2", "hbs": "4.0.1",
"jade": "~1.11.0", "istanbul": "0.4.5",
"marked": "0.3.5", "marked": "0.3.6",
"method-override": "~2.3.5", "method-override": "2.3.8",
"mocha": "2.3.4", "mocha": "3.4.1",
"morgan": "~1.6.1", "morgan": "1.8.1",
"multiparty": "~4.1.2", "multiparty": "4.1.3",
"should": "7.1.1", "pbkdf2-password": "1.2.1",
"supertest": "1.1.0", "should": "11.2.1",
"vhost": "~3.0.1" "supertest": "1.2.0",
"vhost": "~3.0.2"
}, },
"directories": {}, "directories": {},
"dist": { "dist": {
"shasum": "3c0b76f3c77590c8345739061ec0bd3ba067ec24", "shasum": "bab65d0f03aa80c358408972fc700f916944b662",
"tarball": "https://registry.npmjs.org/express/-/express-4.13.4.tgz" "tarball": "https://registry.npmjs.org/express/-/express-4.15.3.tgz"
}, },
"engines": { "engines": {
"node": ">= 0.10.0" "node": ">= 0.10.0"
}, },
"files": [ "files": [
"History.md",
"LICENSE", "LICENSE",
"History.md",
"Readme.md", "Readme.md",
"index.js", "index.js",
"lib/" "lib/"
], ],
"gitHead": "193bed2649c55c1fd362e46cd4702c773f3e7434", "gitHead": "6da454c7fb37e68ed65ffe0371aa688b89f5bd6e",
"homepage": "https://github.com/expressjs/express", "homepage": "http://expressjs.com/",
"keywords": [ "keywords": [
"api",
"app",
"express", "express",
"framework", "framework",
"sinatra",
"web",
"rest", "rest",
"restful", "restful",
"router", "router",
"sinatra", "app",
"web" "api"
], ],
"license": "MIT", "license": "MIT",
"maintainers": [ "maintainers": [
{ {
"name": "dougwilson", "email": "doug@somethingdoug.com",
"email": "doug@somethingdoug.com" "name": "dougwilson"
},
{
"email": "captain@hacksparrow.com",
"name": "hacksparrow"
},
{
"email": "jasnell@gmail.com",
"name": "jasnell"
},
{
"email": "mikeal.rogers@gmail.com",
"name": "mikeal"
} }
], ],
"name": "express", "name": "express",
@ -166,5 +206,5 @@
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/" "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
}, },
"version": "4.13.4" "version": "4.15.3"
} }

9
node_modules/lodash/package.json generated vendored
View File

@ -40,8 +40,13 @@
"_requiredBy": [ "_requiredBy": [
"#USER", "#USER",
"/", "/",
"/cheerio", "/async",
"/gulp-jshint" "/gulp-jshint",
"/gulp-uglify",
"/hipchat-notifier",
"/mailgun-js/async",
"/requestretry",
"/rss-braider"
], ],
"_resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "_resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"_shasum": "78203a4d1c328ae1d86dca6460e369b57f4055ae", "_shasum": "78203a4d1c328ae1d86dca6460e369b57f4055ae",

78
node_modules/rss-braider/.npmignore generated vendored
View File

@ -1,79 +1 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
/dist
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Node template
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules node_modules

9
node_modules/rss-braider/README.md generated vendored
View File

@ -1,4 +1,6 @@
[![Build Status](https://travis-ci.org/KQED/rss-braider.svg?branch=master)](https://travis-ci.org/KQED/rss-braider) [![Build Status](https://travis-ci.org/KQED/rss-braider.svg?branch=master)](https://travis-ci.org/KQED/rss-braider)
[![Dependencies](https://david-dm.org/rKQED/rss-braider.svg?branch=master)](https://david-dm.org/KQED/rss-braider)
## Summary ## Summary
Braid/aggregate one or more RSS feeds (file or url) into a single feed (RSS or JSON output). Process resulting feed through specified plugins. Automatic deduplication Braid/aggregate one or more RSS feeds (file or url) into a single feed (RSS or JSON output). Process resulting feed through specified plugins. Automatic deduplication
@ -8,13 +10,16 @@ Braid/aggregate one or more RSS feeds (file or url) into a single feed (RSS or J
npm install rss-braider npm install rss-braider
``` ```
## Test ## Test
`npm test` ```
npm install
npm test
```
## Examples ## Examples
``` ```
$ cd examples $ cd examples
$ node simple.js (combines 3 sources) $ node simple.js (combines 3 sources)
$ node plugins.js (combines 3 sources and runs a transformation plugin) $ node use_plugins.js (combines 3 sources and runs a transformation plugin)
``` ```
### Code Example ### Code Example
```js ```js

View File

@ -33,8 +33,8 @@ var braider_options = {
}; };
var rss_braider = RssBraider.createClient(braider_options); var rss_braider = RssBraider.createClient(braider_options);
// Set logging level (debug, info, warn, err, off) // Set logging level (debug, info, warn, err)
rss_braider.logger.level('off'); rss_braider.logger.level('info');
rss_braider.processFeed('simple_test_feed', 'rss', function(err, data){ rss_braider.processFeed('simple_test_feed', 'rss', function(err, data){
if (err) { if (err) {

View File

@ -1,206 +1,208 @@
// Process feed-reader item into node-rss item // process feed-reader item into node-rss item
var FeedParser = require('feedparser'); var FeedParser = require('feedparser'),
var bunyan = require('bunyan'); bunyan = require('bunyan'),
var _ = require('lodash'); _ = require('lodash'),
var async = require('async'); async = require('async'),
var request = require('request'); request = require('request'),
var RSS = require('rss'); RSS = require('rss'),
var fs = require('fs'); fs = require('fs'),
var package_json = require('../package.json'); package_json = require('../package.json'),
var logger; logger;
var RssBraider = function(options) { var RssBraider = function (options) {
if (!options) { if (!options) {
options = {}; options = {};
} }
this.feeds = options.feeds || null; this.feeds = options.feeds || null;
this.logger = options.logger || bunyan.createLogger({name: package_json.name}); this.logger = options.logger || bunyan.createLogger({name: package_json.name});
if (options.log_level) { if (options.log_level) {
this.logger.level(options.log_level); this.logger.level(options.log_level);
} }
this.indent = options.indent || ' '; this.indent = options.indent || " ";
this.dedupe_fields = options.dedupe_fields || []; // The fields to use to identify duplicate articles this.dedupe_fields = options.dedupe_fields || []; // The fields to use to identify duplicate articles
this.date_sort_order = options.date_sort_order || 'desc'; this.date_sort_order = options.date_sort_order || "desc";
this.plugins_directories = options.plugins_directories || []; this.plugins_directories = options.plugins_directories || [];
this.plugins = {}; this.plugins = {};
this.loadPlugins(); this.loadPlugins();
}; };
// Loadup self.plugins with the plugin functions // loadup self.plugins with the plugin functions
RssBraider.prototype.loadPlugins = function() { RssBraider.prototype.loadPlugins = function () {
var self = this; var self = this;
if (self.plugins_directories.length < 1) { if (self.plugins_directories.length < 1) {
self.logger.debug('No plugins_directories specified. No plugins loaded.'); self.logger.debug("No plugins_directories specified. No plugins loaded.");
} }
self.plugins_directories.forEach(function(path) { self.plugins_directories.forEach(function(path){
// Load up each file and assign it to the plugins // load up each file and assign it to the plugins
var filenames = fs.readdirSync(path); var filenames = fs.readdirSync(path);
filenames.forEach(function(filename) { filenames.forEach(function(filename){
var plugin_name = filename.replace(/.js$/, ''); var plugin_name = filename.replace(/.js$/, '');
if (self.plugins[plugin_name]) { if (self.plugins[plugin_name]) {
self.logger.warn('Duplicate plugin name: ', plugin_name, 'Overwriting with newer plugin'); self.logger.warn("Duplicate plugin name: ", plugin_name, "Overwriting with newer plugin");
} }
self.plugins[plugin_name] = require(path + '/' + plugin_name); self.plugins[plugin_name] = require(path + '/' + plugin_name);
self.logger.debug('plugin registered:', plugin_name); self.logger.debug("plugin registered:", plugin_name);
}); });
}); });
}; };
RssBraider.prototype.feedExists = function(feed_name) { RssBraider.prototype.feedExists = function (feed_name) {
if (this.feeds && this.feeds[feed_name]) { if (this.feeds && this.feeds[feed_name]) {
return true; return true;
} else { } else {
return false; return false;
} }
}; };
// Gather data from all feed sources, process each article/item through plugins, // Gather data from all feed sources, process each article/item through plugins,
// trim down to desired count, dedupe and sort // trim down to desired count, dedupe and sort
RssBraider.prototype.processFeed = function(feed_name, format, callback) { RssBraider.prototype.processFeed = function(feed_name, format, callback)
var self = this; {
var feed = self.feeds[feed_name]; var self = this,
var feed_articles = []; feed = self.feeds[feed_name],
feed_articles = [];
if (!format) { if (!format) {
format = 'rss'; format = 'rss';
} }
if (!feed || !feed.sources || feed.sources.length < 1) { if (!feed || !feed.sources || feed.sources.length < 1) {
return callback('No definition for feed name: ' + feed_name); return callback("No definition for feed name: " + feed_name);
} }
// Process each feed source through Feedparser to get articles. // Process each feed source through Feedparser to get articles.
// Then process each item/article through rss-braider and any plugins // Then process each item/article through rss-braider and any plugins
async.each(feed.sources, function(source, callback) { async.each(feed.sources, function(source, callback) {
var count = source.count || feed.default_count || 10; var count = source.count || feed.default_count || 10, // Number of articles per source
var url = source.feed_url || null; url = source.feed_url || null,
var file_path = source.file_path || null; file_path = source.file_path || null,
var source_articles = []; source_articles = [];
var feedparser = new FeedParser(); var feedparser = new FeedParser();
if (url) { if (url) {
var req = request(url); var req = request(url);
req.on('error', function(error) { req.on('error', function (error) {
self.logger.error(error); self.logger.error(error);
}); });
req.on('response', function(res) { req.on('response', function (res) {
var stream = this; var stream = this;
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
return this.emit('error', 'Bad status code for ' + url + ': ' + res.statusCode); return this.emit('error', 'Bad status code: ' + res.statusCode);
} }
stream.pipe(feedparser); stream.pipe(feedparser);
}); });
} else if (file_path) { } else if (file_path) {
// Open file // open file
var filestream = fs.createReadStream(file_path); var filestream = fs.createReadStream(file_path);
filestream.pipe(feedparser); filestream.pipe(feedparser);
} else { } else {
self.logger.error('url or file_path not defined for feed: ' + source.name); self.logger.error("url or file_path not defined for feed: " + source.name);
return callback(); return callback();
} }
feedparser.on('error', function(error) { feedparser.on('error', function(error) {
self.logger.error('feedparser',', source.name:', source.name, ', url:', source.feed_url, error.stack); self.logger.error("feedparser",", source.name:", source.name, ", url:", source.feed_url, error.stack);
}); });
// Collect the articles from this source // Collect the articles from this source
feedparser.on('readable', function() { feedparser.on('readable', function() {
var stream = this, var stream = this,
item; item;
while (!!(item = stream.read())) { while ( !!(item = stream.read()) ) {
if (source.feed_url) { if (source.feed_url) {
item.source_url = source.feed_url; item.source_url = source.feed_url;
} }
// Process Item/Article // Process Item/Article
var article = self.processItem(item, source, feed_name); var article = self.processItem(item, source, feed_name);
// Plugins may filter items and return null // plugins may filter items and return null
if (article) { if (article) {
source_articles.push(article); source_articles.push(article);
} }
} }
}); });
feedparser.on('end', function() { feedparser.on("end", function(){
// De-dupe , date sort, and trim this feed's articles and push them into array // de-dupe , date sort, and trim this feed's articles and push them into array
source_articles = self.dedupe(source_articles, self.dedupe_fields); source_articles = self.dedupe(source_articles, self.dedupe_fields);
source_articles = self.date_sort(source_articles); source_articles = self.date_sort(source_articles);
source_articles = source_articles.slice(0, count); source_articles = source_articles.slice(0, count);
feed_articles = feed_articles.concat(source_articles); feed_articles = feed_articles.concat(source_articles);
callback(); callback();
}); });
}, },
function(err) { function(err){
if (err) { if (err) {
self.logger.error(err); self.logger.error(err);
return callback(err); return callback(err);
} else { } else {
// Final Dedupe step and resort // Final Dedupe step and resort
feed_articles = self.dedupe(feed_articles, self.dedupe_fields); feed_articles = self.dedupe(feed_articles, self.dedupe_fields);
feed_articles = self.date_sort(feed_articles); feed_articles = self.date_sort(feed_articles);
// Create new feed with these articles. Follows node-rss spec // Create new feed with these articles. Follows node-rss spec
var options = { var options = {
title: feed.meta.title, title : feed.meta.title,
description: feed.meta.description, description : feed.meta.description,
generator: feed.meta.generator || 'rss-braider', generator : feed.meta.generator || 'rss-braider',
site_url: feed.meta.site_url || null, site_url : feed.meta.site_url || null,
feed_url: feed.meta.feed_url || null, feed_url : feed.meta.feed_url || null,
image_url: feed.meta.image_url || null, image_url : feed.meta.image_url || null,
webMaster: feed.meta.webMaster || null, webMaster : feed.meta.webMaster || null,
copyright: feed.meta.copyright || null, copyright : feed.meta.copyright || null,
categories: feed.meta.categories || null, categories : feed.meta.categories || null,
custom_namespaces: feed.custom_namespaces || [], custom_namespaces : feed.custom_namespaces || [],
no_cdata_fields: feed.no_cdata_fields custom_elements : feed.meta.custom_elements || [],
no_cdata_fields : feed.no_cdata_fields
}; };
var newfeed = new RSS(options, feed_articles); var newfeed = new RSS(options, feed_articles);
var ret_string; var ret_string;
switch (format.toLowerCase()) { switch (format.toLowerCase()) {
case 'json': case 'json':
ret_string = JSON.stringify(newfeed); ret_string = JSON.stringify(newfeed);
break; break;
case 'rss': case 'rss':
ret_string = newfeed.xml(self.indent); ret_string = newfeed.xml(self.indent);
break; break;
default: default:
self.logger.error('Unknown format:', format); self.logger.error("Unknown format:", format);
ret_string = '{}'; ret_string = "{}";
} }
return callback(null, ret_string); return callback(null, ret_string);
} }
}); });
}; };
// Accepts a feed-parser item and builds a node-rss itemOptions object // Accepts a feed-parser item and builds a node-rss itemOptions object
RssBraider.prototype.processItem = function(item, source, feed_name) { RssBraider.prototype.processItem = function (item, source, feed_name) {
var self = this; var self = this;
if (!item || !source || !feed_name) { if (!item || !source || !feed_name) {
self.logger.error('processItem: missing item, source, and/or feed_name'); self.logger.error("processItem: missing item, source, and/or feed_name");
return null; return null;
} }
// Basics // Basics
var itemOptions = { var itemOptions = {
title: item.title, title : item.title,
description: item.summary, description : item.summary,
url: item.link, url : item.link,
guid: item.guid, guid : item.guid,
permalink: item.permalink, permalink : item.permalink,
author: item.author, author : item.author,
date: item.date, date : item.date,
categories: item.categories, categories : item.categories,
custom_elements: [] custom_elements : []
}; };
// Run the plugins specified by the "plugins" section of the // Run the plugins specified by the "plugins" section of the
// feed .js file to build out any custom elements or // feed .js file to build out any custom elements or
@ -208,9 +210,9 @@ RssBraider.prototype.processItem = function(item, source, feed_name) {
var filteredItemOptions = self.runPlugins(item, itemOptions, source, feed_name); var filteredItemOptions = self.runPlugins(item, itemOptions, source, feed_name);
return filteredItemOptions; return filteredItemOptions;
}; };
RssBraider.prototype.runPlugins = function(item, itemOptions, source, feed_name) { RssBraider.prototype.runPlugins = function (item, itemOptions, source, feed_name) {
var self = this, var self = this,
feed = self.feeds[feed_name] || {}, feed = self.feeds[feed_name] || {},
plugins_list = feed.plugins || [], plugins_list = feed.plugins || [],
@ -220,77 +222,77 @@ RssBraider.prototype.runPlugins = function(item, itemOptions, source, feed_name)
// Process the item through the desired feed plugins // Process the item through the desired feed plugins
// plugins_list.forEach(function(plugin_name){ // plugins_list.forEach(function(plugin_name){
for (var i = 0; i < plugins_list.length; i++) { for (var i = 0; i < plugins_list.length; i++) {
var plugin_name = plugins_list[i]; var plugin_name = plugins_list[i];
if (self.plugins[plugin_name]) { if (self.plugins[plugin_name]) {
filteredItemOptions = self.plugins[plugin_name](item, itemOptions, source); filteredItemOptions = self.plugins[plugin_name](item, itemOptions, source);
} else { } else {
self.logger.error('A plugin named \'' + plugin_name + '\' hasn\'t been registered'); self.logger.error("A plugin named '" + plugin_name + "' hasn't been registered");
} }
// A plugin returning -1 means skip this item // A plugin returning -1 means skip this item
if (filteredItemOptions === -1) { if (filteredItemOptions === -1) {
self.logger.debug('Plugin \'' + plugin_name + '\' filtered item from feed \'' + feed.meta.title + '\'', item.guid); self.logger.debug("Plugin '" + plugin_name + "' filtered item from feed '" + feed.meta.title + "'", item.guid);
itemOptions = null; itemOptions = null;
break; break;
} }
// Check that the plugin didn't just return null or undef, which would be bad. // Check that the plugin didn't just return null or undef, which would be bad.
if (!filteredItemOptions) { if (!filteredItemOptions) {
self.logger.debug('Plugin \'' + plugin_name + '\' failed to return itemOptions for feed:\'' + feed.meta.title + '\'', item.guid); self.logger.debug("Plugin '" + plugin_name + "' failed to return itemOptions for feed:'" + feed.meta.title + "'", item.guid);
filteredItemOptions = itemOptions; // Reset filteredItemOptions = itemOptions; // Reset
} }
// Prepare for next plugin. // Prepare for next plugin.
itemOptions = filteredItemOptions; itemOptions = filteredItemOptions;
} }
return itemOptions; return itemOptions;
}; };
// Dedupe articles in node-rss itemOptions format // Dedupe articles in node-rss itemOptions format
// Accepts an array of fields to dedupe on, or does a basic uniq // Accepts an array of fields to dedupe on, or does a basic uniq
// operation on the articles array // operation on the articles array
// TODO, make this a plugin? // TODO, make this a plugin?
RssBraider.prototype.dedupe = function(articles_arr, fields) { RssBraider.prototype.dedupe = function(articles_arr, fields){
var self = this; var self = this;
if (!fields || fields.length < 1) { if ( !fields || fields.length < 1 ) {
return _.uniq(articles_arr); return _.uniq(articles_arr);
} else { } else {
var uniques = {}, var uniques = {},
deduped_articles = []; deduped_articles = [];
articles_arr.forEach(function(article) { articles_arr.forEach(function(article){
var count = 0; var count = 0;
fields.forEach(function(field) { fields.forEach(function(field){
if (!uniques[field]) { if (!uniques[field]) {
uniques[field] = []; uniques[field] = [];
} }
if (uniques[field].indexOf(article[field]) !== -1) { if (uniques[field].indexOf(article[field]) !== -1) {
count++; count++;
} else { } else {
uniques[field].push(article[field]); uniques[field].push(article[field]);
} }
}); });
if (count !== fields.length) { if (count !== fields.length) {
// It's unique // it's unique
deduped_articles.push(article); deduped_articles.push(article);
} else { } else {
// The article matched all of another article's "dedupe" fields // The article matched all of another article's "dedupe" fields
// so filter it out (i.e. do nothing) // so filter it out (i.e. do nothing)
self.logger.debug('skipping duplicate', '"' + article.title + '"', article.guid); self.logger.debug("skipping duplicate", '"' + article.title + '"', article.guid);
} }
}); });
return deduped_articles; return deduped_articles;
} }
}; };
// TODO: Could be a plugin // TODO: Could be a plugin
// Sort articles by date // Sort articles by date
RssBraider.prototype.date_sort = function(articles_arr) { RssBraider.prototype.date_sort = function(articles_arr) {
var sorted_articles = _.sortBy(articles_arr, function(article) { var sorted_articles = _.sortBy(articles_arr, function(article) {
return article.date.getTime(); return article.date.getTime();
}); });
if (this.date_sort_order === 'desc') { if (this.date_sort_order === "desc") {
sorted_articles.reverse(); sorted_articles.reverse();
} }
return sorted_articles; return sorted_articles;
}; };
module.exports = RssBraider; module.exports = RssBraider;

View File

@ -2,37 +2,47 @@
"_args": [ "_args": [
[ [
{ {
"name": null, "name": "rss-braider",
"raw": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "raw": "rss-braider",
"rawSpec": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "rawSpec": "",
"scope": null, "scope": null,
"spec": "http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "spec": "latest",
"type": "git" "type": "tag"
}, },
"/home/martind2000/dev/Rinser" "/home/martind2000/dev/Rinser"
] ]
], ],
"_from": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "_from": "rss-braider@latest",
"_id": "rss-braider@1.0.0", "_id": "rss-braider@1.2.0",
"_inCache": true, "_inCache": true,
"_installable": true, "_installable": true,
"_location": "/rss-braider", "_location": "/rss-braider",
"_nodeVersion": "6.2.1",
"_npmOperationalInternal": {
"host": "packages-18-east.internal.npmjs.com",
"tmp": "tmp/rss-braider-1.2.0.tgz_1487633154488_0.3144866321235895"
},
"_npmUser": {
"email": "kgebhardt23@gmail.com",
"name": "rv-kip"
},
"_npmVersion": "3.9.3",
"_phantomChildren": {}, "_phantomChildren": {},
"_requested": { "_requested": {
"name": null, "name": "rss-braider",
"raw": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "raw": "rss-braider",
"rawSpec": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "rawSpec": "",
"scope": null, "scope": null,
"spec": "http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "spec": "latest",
"type": "git" "type": "tag"
}, },
"_requiredBy": [ "_requiredBy": [
"#DEV:/" "/"
], ],
"_resolved": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git#1559131d2e2d73076307667d260f0e173e64c8c3", "_resolved": "https://registry.npmjs.org/rss-braider/-/rss-braider-1.2.0.tgz",
"_shasum": "4ca2443627b6ffd330424d220aa10635f4f8cb8c", "_shasum": "ce134542a39b643b916b95dd08a10dbfd7f1c28b",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git", "_spec": "rss-braider",
"_where": "/home/martind2000/dev/Rinser", "_where": "/home/martind2000/dev/Rinser",
"author": { "author": {
"email": "kgebhardt@kqed.org", "email": "kgebhardt@kqed.org",
@ -43,20 +53,26 @@
"url": "http://github.com/KQED/rss-braider/issues" "url": "http://github.com/KQED/rss-braider/issues"
}, },
"dependencies": { "dependencies": {
"async": "^1.2.1", "async": "^2.1.5",
"bunyan": "^1.4.0", "bunyan": "^1.4.0",
"feedparser": "^1.1.3", "feedparser": "^2.1.0",
"include-folder": "^0.9.0", "include-folder": "^1.0.0",
"lodash": "^3.9.3", "lodash": "^4.17.4",
"request": "^2.57.0", "request": "^2.57.0",
"rss": "git://github.com/rv-kip/node-rss.git#8d1420" "rss": "1.2.0"
}, },
"description": "Braid/aggregate/combine RSS feeds into a single RSS (or JSON) document. Optionally process through specified plugins.", "description": "Braid/aggregate/combine RSS feeds into a single RSS (or JSON) document. Optionally process through specified plugins.",
"devDependencies": { "devDependencies": {
"mockdate": "^1.0.3", "mockdate": "^2.0.1",
"tape": "^4.0.0" "tape": "^4.0.0"
}, },
"gitHead": "1559131d2e2d73076307667d260f0e173e64c8c3", "directories": {},
"dist": {
"shasum": "ce134542a39b643b916b95dd08a10dbfd7f1c28b",
"tarball": "https://registry.npmjs.org/rss-braider/-/rss-braider-1.2.0.tgz"
},
"gitHead": "898ec8743c4ab37c4bf7a8fc925356c351d7df39",
"homepage": "https://github.com/KQED/rss-braider#readme",
"keywords": [ "keywords": [
"rss", "rss",
"braider", "braider",
@ -77,16 +93,21 @@
], ],
"license": "MIT", "license": "MIT",
"main": "index.js", "main": "index.js",
"maintainers": [
{
"email": "kgebhardt23@gmail.com",
"name": "rv-kip"
}
],
"name": "rss-braider", "name": "rss-braider",
"optionalDependencies": {}, "optionalDependencies": {},
"readme": "[![Build Status](https://travis-ci.org/KQED/rss-braider.svg?branch=master)](https://travis-ci.org/KQED/rss-braider)\n\n## Summary\nBraid/aggregate one or more RSS feeds (file or url) into a single feed (RSS or JSON output). Process resulting feed through specified plugins. Automatic deduplication\n\n## Installation\n```\nnpm install rss-braider\n```\n## Test\n`npm test`\n\n## Examples\n```\n$ cd examples\n$ node simple.js (combines 3 sources)\n$ node plugins.js (combines 3 sources and runs a transformation plugin)\n```\n### Code Example\n```js\nvar RssBraider = require('rss-braider'),\n feeds = {};\n\n// Pull feeds from config files:\n// feeds.simple_test_feed = require(\"./config/feed\").feed;\n// Or define in-line\nfeeds.simple_test_feed = {\n \"feed_name\" : \"feed\",\n \"default_count\" : 1,\n \"no_cdata_fields\" : [], // Don't wrap these fields in CDATA tags\n \"meta\" : {\n \"title\": \"NPR Braided Feed\",\n \"description\": \"This is a test of two NPR\"\n },\n \"sources\" : [\n {\n \"name\" : \"NPR Headlines\",\n \"count\" : 2,\n \"feed_url\" : \"http://www.npr.org/rss/rss.php?id=1001\",\n },\n {\n \"name\" : \"NPR Sports\",\n \"count\" : 2,\n \"feed_url\" : \"http://www.npr.org/rss/rss.php?id=1055\"\n }\n ]\n};\nvar braider_options = {\n feeds : feeds,\n indent : \" \",\n date_sort_order : \"desc\", // Newest first\n log_level : \"debug\"\n};\nvar rss_braider = RssBraider.createClient(braider_options);\n\n// Override logging level (debug, info, warn, err, off)\nrss_braider.logger.level('off');\n\n// Output braided feed as rss. use 'json' for JSON output.\nrss_braider.processFeed('simple_test_feed', 'rss', function(err, data){\n if (err) {\n return console.log(err);\n }\n console.log(data);\n});\n```\n## Plugins\nPlugins provide custom manipulation and filtering of RSS items/articles. See `examples/plugins` for examples.\n\nA plugin operates by modifying the `itemOptions` object or by returning `null` which will exclude the `item` (article) from the resulting feed (See `examples/plugins/filter_out_all_articles.js`).\n\nThe `itemsOptions` object gets passed to `node-rss` to generate the RSS feeds, so read the documentation on that module and its use of custom namespaces. (https://github.com/dylang/node-rss)\n\n### Plugin Example\nThis plugin will capitalize the article title for all articles\n```js\nmodule.exports = function (item, itemOptions, source) {\n if (!item || !itemOptions) {\n return;\n }\n\n if (itemOptions.title) {\n itemOptions.title = itemOptions.title.toUpperCase();\n }\n\n return itemOptions;\n};\n```\n\nThe plugin is registered with the feed in the feed config .js file and are run in order.\n```js\nvar feed = {\n \"feed_name\" : \"feed with plugins\",\n \"default_count\" : 1,\n \"plugins\" : ['capitalize_title', 'plugin_template'],\n...\n```\n\n## Release Notes\n### 1.0.0\nChanged plugin architecture to allow filtering out of article/items by returning `-1` instead of a modified `itemsOptions` object. This is a breaking change as it will require existing plugins to return `itemsOptions` instead of modifying the reference. See `examples/plugins`.\n\n", "readme": "ERROR: No README data found!",
"readmeFilename": "README.md",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git" "url": "git+https://github.com/KQED/rss-braider.git"
}, },
"scripts": { "scripts": {
"test": "tape test" "test": "tape test"
}, },
"version": "1.0.0" "version": "1.2.0"
} }

View File

@ -1,15 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel> <channel>
<title><![CDATA[Test File Feed]]></title> <title><![CDATA[Test File Feed]]></title>
<description>This feed comes from a file</description> <description><![CDATA[This feed comes from a file]]></description>
<link>http://github.com/dylang/node-rss</link> <link>http://github.com/dylang/node-rss</link>
<generator>rss-braider</generator> <generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate> <lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item> <item>
<title><![CDATA[Rent Hike For Dance Mission Theater Has Artists Worried About Uncertain Future]]></title> <title><![CDATA[Rent Hike For Dance Mission Theater Has Artists Worried About Uncertain Future]]></title>
<description>&lt;p&gt;Stepping out of BART at 24th and Mission at most hours of the day, one is likely to hear the pulse of African drums, hip-hop or salsa emanating from the second-floor studios of Dance Brigade&apos;s Dance Mission Theater. But that music may not continue forever.&lt;/p&gt; <description><![CDATA[<p>Stepping out of BART at 24th and Mission at most hours of the day, one is likely to hear the pulse of African drums, hip-hop or salsa emanating from the second-floor studios of Dance Brigade's Dance Mission Theater. But that music may not continue forever.</p>
&lt;p&gt;The performance space and dance school &lt;a href=&quot;http://ww2.kqed.org/news/2014/12/20/dance-mission-theater-rent-increase-worries-artists/&quot; target=&quot;_self&quot; id=&quot;rssmi_more&quot;&gt; ...read more&lt;/a&gt;</description> <p>The performance space and dance school <a href="http://ww2.kqed.org/news/2014/12/20/dance-mission-theater-rent-increase-worries-artists/" target="_self" id="rssmi_more"> ...read more</a>]]></description>
<link>http://ww2.kqed.org/news/2014/12/20/dance-mission-theater-rent-increase-worries-artists/</link> <link>http://ww2.kqed.org/news/2014/12/20/dance-mission-theater-rent-increase-worries-artists/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/2014/12/20/rent-hike-for-dance-mission-theater-has-artists-worried-about-uncertain-future/</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/2014/12/20/rent-hike-for-dance-mission-theater-has-artists-worried-about-uncertain-future/</guid>
<dc:creator><![CDATA[KQED Arts]]></dc:creator> <dc:creator><![CDATA[KQED Arts]]></dc:creator>
@ -27,7 +26,7 @@
</item> </item>
<item> <item>
<title><![CDATA[Bob Miller: Teasing Science Lessons from Everyday Phenomena]]></title> <title><![CDATA[Bob Miller: Teasing Science Lessons from Everyday Phenomena]]></title>
<description>Until February 5, 2015, you can visit the main branch of the San Francisco Public Library and be momentarily transported through space and time to the early days of the Exploratorium via the life and work of Bob Miller, who died in 2007.</description> <description><![CDATA[Until February 5, 2015, you can visit the main branch of the San Francisco Public Library and be momentarily transported through space and time to the early days of the Exploratorium via the life and work of Bob Miller, who died in 2007.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/20/bob-miller-teasing-science-lessons-from-everyday-phenomena/</link> <link>http://ww2.kqed.org/arts/2014/12/20/bob-miller-teasing-science-lessons-from-everyday-phenomena/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10220517</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10220517</guid>
<dc:creator><![CDATA[Sarah Hotchkiss]]></dc:creator> <dc:creator><![CDATA[Sarah Hotchkiss]]></dc:creator>
@ -55,7 +54,7 @@
</item> </item>
<item> <item>
<title><![CDATA[Light Art Brings Holiday Glow to Darkest Nights]]></title> <title><![CDATA[Light Art Brings Holiday Glow to Darkest Nights]]></title>
<description>In the dark of winter, San Franciscans with an urge to celebrate the light can visit a new wealth of illuminated art installations. This video tour offers a preview of some of the more dazzling works.</description> <description><![CDATA[In the dark of winter, San Franciscans with an urge to celebrate the light can visit a new wealth of illuminated art installations. This video tour offers a preview of some of the more dazzling works.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/21/on-darkest-nights-illuminated-art-brings-holiday-glow/</link> <link>http://ww2.kqed.org/arts/2014/12/21/on-darkest-nights-illuminated-art-brings-holiday-glow/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10231062</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10231062</guid>
<dc:creator><![CDATA[KQED Arts]]></dc:creator> <dc:creator><![CDATA[KQED Arts]]></dc:creator>
@ -72,7 +71,7 @@
</item> </item>
<item> <item>
<title><![CDATA[93 Til Infinity: Watch Bay Area Musicians Remix Classic 90s Hip-Hop]]></title> <title><![CDATA[93 Til Infinity: Watch Bay Area Musicians Remix Classic 90s Hip-Hop]]></title>
<description>One night back in November, the YBCA took a trip to 1993 to celebrate a groundbreaking year for hip hop.</description> <description><![CDATA[One night back in November, the YBCA took a trip to 1993 to celebrate a groundbreaking year for hip hop.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/22/93-til-infinity-watch-bay-area-musicians-remix-classic-90s-hip-hop/</link> <link>http://ww2.kqed.org/arts/2014/12/22/93-til-infinity-watch-bay-area-musicians-remix-classic-90s-hip-hop/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10230592</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10230592</guid>
<dc:creator><![CDATA[Kevin L. Jones]]></dc:creator> <dc:creator><![CDATA[Kevin L. Jones]]></dc:creator>
@ -91,7 +90,7 @@
</item> </item>
<item> <item>
<title><![CDATA[Protest Icons: Not Just for Show]]></title> <title><![CDATA[Protest Icons: Not Just for Show]]></title>
<description>Hands up, umbrellas out, hoodies on, fists raised -- the icons of protest have long played a significant role in movements of social change and revolution. Some of the most potent protest icons of 2014 appeared in just the last four months of the year.</description> <description><![CDATA[Hands up, umbrellas out, hoodies on, fists raised -- the icons of protest have long played a significant role in movements of social change and revolution. Some of the most potent protest icons of 2014 appeared in just the last four months of the year.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/23/protest-icons-not-just-for-show/</link> <link>http://ww2.kqed.org/arts/2014/12/23/protest-icons-not-just-for-show/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10189953</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10189953</guid>
<dc:creator><![CDATA[Michele Carlson]]></dc:creator> <dc:creator><![CDATA[Michele Carlson]]></dc:creator>

View File

@ -1,14 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel> <channel>
<title><![CDATA[Test File Feed]]></title> <title><![CDATA[Test File Feed]]></title>
<description>This feed comes from a file</description> <description><![CDATA[This feed comes from a file]]></description>
<link>http://github.com/dylang/node-rss</link> <link>http://github.com/dylang/node-rss</link>
<generator>rss-braider</generator> <generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate> <lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item> <item>
<title><![CDATA[Top 10 Movie Moments of 2014]]></title> <title><![CDATA[Top 10 Movie Moments of 2014]]></title>
<description>A handful of scenes resonate from a mediocre year for film.</description> <description><![CDATA[A handful of scenes resonate from a mediocre year for film.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/26/top-10-movie-moments-of-2014/</link> <link>http://ww2.kqed.org/arts/2014/12/26/top-10-movie-moments-of-2014/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid>
<dc:creator><![CDATA[Michael Fox]]></dc:creator> <dc:creator><![CDATA[Michael Fox]]></dc:creator>
@ -56,7 +55,7 @@
</item> </item>
<item> <item>
<title><![CDATA[Radio Show: Rockin Roots to Radical Rauschenberg]]></title> <title><![CDATA[Radio Show: Rockin Roots to Radical Rauschenberg]]></title>
<description>Cy Musiker and David Wiegand share their picks for great events around the Bay Area this week.</description> <description><![CDATA[Cy Musiker and David Wiegand share their picks for great events around the Bay Area this week.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/25/radio-show-rockin-roots-to-radical-rauschenberg/</link> <link>http://ww2.kqed.org/arts/2014/12/25/radio-show-rockin-roots-to-radical-rauschenberg/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238611</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238611</guid>
<dc:creator><![CDATA[The Do List]]></dc:creator> <dc:creator><![CDATA[The Do List]]></dc:creator>
@ -98,7 +97,7 @@
</item> </item>
<item> <item>
<title><![CDATA[Watch a Hip Hop Orchestra Perform Wu-Tang Clans “C.R.E.A.M”]]></title> <title><![CDATA[Watch a Hip Hop Orchestra Perform Wu-Tang Clans “C.R.E.A.M”]]></title>
<description>Above: Ensemble Mik Nawooj (EMN), led by music director, composer/pianist JooWan Kim, reinterprets Wu-Tang Clans &amp;#8220;C.R.E.A.M.&amp;#8221; at YBCA&amp;#8217;s Clas/Sick Hip Hop: 1993 Edition. Ensemble Mik Nawooj (EMN) is the brainchild of composer/pianist JooWan Kim, who introduces western-European classical techniques into hip-hop, rock, and pop. The group&amp;#8217;s lineup includes traditional Pierrot ensemble instrumentation (flute, clarinet, violin, cello and piano) with [&amp;#8230;]</description> <description><![CDATA[Above: Ensemble Mik Nawooj (EMN), led by music director, composer/pianist JooWan Kim, reinterprets Wu-Tang Clans &#8220;C.R.E.A.M.&#8221; at YBCA&#8217;s Clas/Sick Hip Hop: 1993 Edition. Ensemble Mik Nawooj (EMN) is the brainchild of composer/pianist JooWan Kim, who introduces western-European classical techniques into hip-hop, rock, and pop. The group&#8217;s lineup includes traditional Pierrot ensemble instrumentation (flute, clarinet, violin, cello and piano) with [&#8230;]]]></description>
<link>http://ww2.kqed.org/arts/2014/12/24/a-hip-hop-orchestra-does-wu-tang-clans-c-r-e-a-m/</link> <link>http://ww2.kqed.org/arts/2014/12/24/a-hip-hop-orchestra-does-wu-tang-clans-c-r-e-a-m/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238600</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238600</guid>
<dc:creator><![CDATA[Siouxsie Oki]]></dc:creator> <dc:creator><![CDATA[Siouxsie Oki]]></dc:creator>
@ -118,7 +117,7 @@
</item> </item>
<item> <item>
<title><![CDATA[Watch a Hip-Hop Funk Big Band Pay Homage to Saafirs “Light Sleeper”]]></title> <title><![CDATA[Watch a Hip-Hop Funk Big Band Pay Homage to Saafirs “Light Sleeper”]]></title>
<description>Above: Kev Choice Ensemble reinterprets Saafir&amp;#8217;s &amp;#8220;Light Sleeper&amp;#8221; at YBCA&amp;#8217;s Clas/Sick Hip Hop: 1993 Edition. Oakland-based artist Kev Choice is a pianist, MC, producer, bandleader, sideman, music historian and urban griot dedicated to cultural expression. Through his band Kev Choice Ensemble, he produces and performs his own material, including original jazz and classical compositions as well as classical, [&amp;#8230;]</description> <description><![CDATA[Above: Kev Choice Ensemble reinterprets Saafir&#8217;s &#8220;Light Sleeper&#8221; at YBCA&#8217;s Clas/Sick Hip Hop: 1993 Edition. Oakland-based artist Kev Choice is a pianist, MC, producer, bandleader, sideman, music historian and urban griot dedicated to cultural expression. Through his band Kev Choice Ensemble, he produces and performs his own material, including original jazz and classical compositions as well as classical, [&#8230;]]]></description>
<link>http://ww2.kqed.org/arts/2014/12/24/a-jazz-hip-hop-funk-big-band-pays-homage-to-saafirs-light-sleeper/</link> <link>http://ww2.kqed.org/arts/2014/12/24/a-jazz-hip-hop-funk-big-band-pays-homage-to-saafirs-light-sleeper/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238610</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238610</guid>
<dc:creator><![CDATA[Siouxsie Oki]]></dc:creator> <dc:creator><![CDATA[Siouxsie Oki]]></dc:creator>
@ -139,7 +138,7 @@
</item> </item>
<item> <item>
<title><![CDATA[Jason McHenry and His Quest for One Million Paintings]]></title> <title><![CDATA[Jason McHenry and His Quest for One Million Paintings]]></title>
<description>Though the original intent was to make as much art as possible, the project somehow morphed into a goal of producing a seemingly preposterous number of paintings; now the mission of the &lt;em&gt;One Thousand Thousand&lt;/em&gt; project is to amass that amount with the help of friends.</description> <description><![CDATA[Though the original intent was to make as much art as possible, the project somehow morphed into a goal of producing a seemingly preposterous number of paintings; now the mission of the <em>One Thousand Thousand</em> project is to amass that amount with the help of friends.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/24/jason-mchenry-and-his-quest-to-amass-one-million-paintings/</link> <link>http://ww2.kqed.org/arts/2014/12/24/jason-mchenry-and-his-quest-to-amass-one-million-paintings/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10218934</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10218934</guid>
<dc:creator><![CDATA[Brian Eder]]></dc:creator> <dc:creator><![CDATA[Brian Eder]]></dc:creator>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel> <channel>
<title><![CDATA[A Feed with no elements]]></title> <title><![CDATA[A Feed with no elements]]></title>
<description><![CDATA[This feed will have no elements as a result of the filter_all_articles plugin. Used for unit tests.]]></description> <description><![CDATA[This feed will have no elements as a result of the filter_all_articles plugin. Used for unit tests.]]></description>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel> <channel>
<title><![CDATA[A Feed with no elements]]></title> <title><![CDATA[A Feed with no elements]]></title>
<description><![CDATA[This feed will have no elements as a result of the filter_all_articles plugin. Used for unit tests.]]></description> <description><![CDATA[This feed will have no elements as a result of the filter_all_articles plugin. Used for unit tests.]]></description>

View File

@ -1,14 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel> <channel>
<title><![CDATA[Test File Feed]]></title> <title><![CDATA[Test File Feed]]></title>
<description>This feed comes from a file</description> <description><![CDATA[This feed comes from a file]]></description>
<link>http://github.com/dylang/node-rss</link> <link>http://github.com/dylang/node-rss</link>
<generator>rss-braider</generator> <generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate> <lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item> <item>
<title><![CDATA[Top 10 Movie Moments of 2014]]></title> <title><![CDATA[Top 10 Movie Moments of 2014]]></title>
<description>A handful of scenes resonate from a mediocre year for film.</description> <description><![CDATA[A handful of scenes resonate from a mediocre year for film.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/26/top-10-movie-moments-of-2014/</link> <link>http://ww2.kqed.org/arts/2014/12/26/top-10-movie-moments-of-2014/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid>
<dc:creator><![CDATA[Michael Fox]]></dc:creator> <dc:creator><![CDATA[Michael Fox]]></dc:creator>

View File

@ -1,14 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:ev="http://purl.org/rss/2.0/modules/event/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel> <channel>
<title><![CDATA[Test File Feed]]></title> <title><![CDATA[Test File Feed]]></title>
<description>This feed comes from a file</description> <description><![CDATA[This feed comes from a file]]></description>
<link>http://github.com/dylang/node-rss</link> <link>http://github.com/dylang/node-rss</link>
<generator>rss-braider</generator> <generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate> <lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item> <item>
<title><![CDATA[Top 10 Movie Moments of 2014]]></title> <title><![CDATA[Top 10 Movie Moments of 2014]]></title>
<description>A handful of scenes resonate from a mediocre year for film.</description> <description><![CDATA[A handful of scenes resonate from a mediocre year for film.]]></description>
<link>http://ww2.kqed.org/arts/2014/12/26/top-10-movie-moments-of-2014/</link> <link>http://ww2.kqed.org/arts/2014/12/26/top-10-movie-moments-of-2014/</link>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid> <guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid>
<dc:creator><![CDATA[Michael Fox]]></dc:creator> <dc:creator><![CDATA[Michael Fox]]></dc:creator>

View File

@ -23,7 +23,6 @@ test('generate feed. No plugins', function(t) {
} }
// console.log(data); // console.log(data);
t.equal(data, expectedOutput.fileFeedOutput); t.equal(data, expectedOutput.fileFeedOutput);
}); });
}); });

View File

@ -2,43 +2,43 @@
"name": "Rinser", "name": "Rinser",
"version": "0.1.1", "version": "0.1.1",
"devDependencies": { "devDependencies": {
"cheerio": "^0.20.0", "cheerio": "^0.22.0",
"del": "^2.2.0", "del": "^2.2.0",
"ejs": "^2.3.3", "ejs": "^2.5.6",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0", "gulp-autoprefixer": "^4.0.0",
"gulp-cache": "^0.4.2", "gulp-cache": "^0.4.6",
"gulp-concat": "^2.6.0", "gulp-concat": "^2.6.1",
"gulp-cssnano": "^2.1.1", "gulp-cssnano": "^2.1.1",
"gulp-html-replace": "^1.5.5", "gulp-html-replace": "^1.6.2",
"gulp-htmlmin": "^1.3.0", "gulp-htmlmin": "^3.0.0",
"gulp-inject": "^3.0.0", "gulp-inject": "^4.2.0",
"gulp-jshint": "^2.0.0", "gulp-jshint": "^2.0.4",
"gulp-livereload": "^3.8.1", "gulp-livereload": "^3.8.1",
"gulp-notify": "^2.2.0", "gulp-notify": "^3.0.0",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-uglify": "^1.5.3", "gulp-uglify": "^3.0.0",
"jshint": "^2.9.1", "jshint": "^2.9.5",
"jsonfile": "^2.4.0", "jsonfile": "^3.0.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"log4js": "^0.6.38", "log4js": "^2.2.0",
"rss-braider": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"stopwords-en": "^0.3.0", "stopwords-en": "^0.3.0",
"striptags": "^3.0.1" "striptags": "^3.0.1"
}, },
"dependencies": { "dependencies": {
"body-parser": "^1.14.2", "body-parser": "^1.17.2",
"cookie-parser": "*", "cookie-parser": "*",
"ejs": "*", "ejs": "*",
"errorhandler": "*", "errorhandler": "^1.5.0",
"express": "^4.13.4", "express": "^4.15.3",
"express-session": "*", "express-session": "^1.15.3",
"htmlparser": "^1.7.7", "htmlparser": "^1.7.7",
"jsonfile": "^2.4.0", "jsonfile": "^2.4.0",
"lodash": "^4.16.6", "lodash": "^4.16.6",
"method-override": "*", "method-override": "^2.3.9",
"morgan": "*", "morgan": "^1.8.2",
"request": "*", "request": "^2.81.0",
"yargs": "^6.3.0" "rss-braider": "^1.2.0",
"yargs": "^8.0.2"
} }
} }

File diff suppressed because one or more lines are too long