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')
, buildOpts = {
printStdout: true
, printStderr: true
};
var fs = require('fs');
var execSync = require('child_process').execSync;
var exec = function (cmd) {
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.');
});
@ -12,35 +14,58 @@ desc('Cleans browerified/minified files and package files');
task('clean', ['clobber'], function () {
jake.rmRf('./ejs.js');
jake.rmRf('./ejs.min.js');
console.log('Cleaned up compiled files.');
});
task('browserify', {async: true}, function () {
jake.exec('./node_modules/browserify/bin/cmd.js lib/ejs.js > ejs.js',
buildOpts, function () {
console.log('Browserification completed.');
setTimeout(complete, 0);
});
desc('Lints the source code');
task('lint', function () {
exec('./node_modules/.bin/eslint \"**/*.js\" Jakefile');
console.log('Linting completed.');
});
task('minify', {async: true}, function () {
jake.exec('./node_modules/uglify-js/bin/uglifyjs ejs.js > ejs.min.js',
buildOpts, function () {
console.log('Minification completed.');
setTimeout(complete, 0);
});
task('browserify', function () {
exec('./node_modules/browserify/bin/cmd.js --standalone ejs lib/ejs.js > ejs.js');
console.log('Browserification completed.');
});
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 () {
this.packageFiles.include([
'Jakefile'
, 'README.md'
, 'LICENSE'
, 'package.json'
, 'ejs.js'
, 'ejs.min.js'
, 'lib/**'
, 'test/**'
'Jakefile',
'README.md',
'LICENSE',
'package.json',
'ejs.js',
'ejs.min.js',
'lib/**',
'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();
});

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

@ -3,7 +3,7 @@
Embedded JavaScript templates
[![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
@ -18,7 +18,7 @@ $ npm install ejs
* Unescaped raw output with `<%- %>`
* Newline-trim mode ('newline slurping') with `-%>` ending tag
* 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
* Client-side support
* Static caching of intermediate JavaScript
@ -33,6 +33,8 @@ $ npm install ejs
<% } %>
```
Try EJS online at: https://ionicabizau.github.io/ejs-playground/.
## Usage
```javascript
@ -50,19 +52,20 @@ ejs.renderFile(filename, data, options, function(err, str){
It is also possible to use `ejs.render(dataAndOptions);` where you pass
everything in a single object. In that case, you'll end up with local variables
for all the passed options. However, be aware that your code could break if we
for all the passed options. However, be aware that your code could break if we
add an option with the same name as one of your data object's properties.
Therefore, we do not recommend using this shortcut.
## Options
- `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.
- `root` Set project root for includes with an absolute path (/file.ejs).
- `context` Function execution context
- `compileDebug` When `false` no debug instrumentation is compiled
- `client` When `true`, compiles a function that can be rendered
in the browser without needing to load the EJS Runtime
- `client` When `true`, compiles a function that can be rendered
in the browser without needing to load the EJS Runtime
([ejs.min.js](https://github.com/mde/ejs/releases/latest)).
- `delimiter` Character to use with angle brackets for open/close
- `debug` Output generated function body
@ -76,9 +79,9 @@ Therefore, we do not recommend using this shortcut.
- `escape` The escaping function used with `<%=` construct. It is
used in rendering and is `.toString()`ed in the generation of client functions. (By default escapes XML).
This project uses [JSDoc](http://usejsdoc.org/). For the full public API
documentation, clone the repository and run `npm run doc`. This will run JSDoc
with the proper options and output the documentation to `out/`. If you want
This project uses [JSDoc](http://usejsdoc.org/). For the full public API
documentation, clone the repository and run `npm run doc`. This will run JSDoc
with the proper options and output the documentation to `out/`. If you want
the both the public & private API docs, run `npm run devdoc` instead.
## Tags
@ -89,6 +92,7 @@ the both the public & private API docs, run `npm run devdoc` instead.
- `<%-` Outputs the unescaped value into the template
- `<%#` Comment tag, no execution, no output
- `<%%` Outputs a literal '<%'
- `%%>` Outputs a literal '%>'
- `%>` Plain ending tag
- `-%>` Trim-mode ('newline slurp') tag, trims following newline
- `_%>` 'Whitespace Slurping' ending tag, removes all whitespace after it
@ -98,11 +102,11 @@ For the full syntax documentation, please see [docs/syntax.md](https://github.co
## Includes
Includes either have to be an absolute path, or, if not, are assumed as
relative to the template with the `include` call. For example if you are
including `./views/user/show.ejs` from `./views/users.ejs` you would
relative to the template with the `include` call. For example if you are
including `./views/user/show.ejs` from `./views/users.ejs` you would
use `<%- include('user/show') %>`.
You must specify the `filename` option for the template with the `include`
You must specify the `filename` option for the template with the `include`
call unless you are using `renderFile()`.
You'll likely want to use the raw output tag (`<%-`) with your include to avoid
@ -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
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
EJS does not specifically support blocks, but layouts can be implemented by
@ -178,8 +197,8 @@ including headers and footers, like so:
## Client-side support
Go to the [Latest Release](https://github.com/mde/ejs/releases/latest), download
`./ejs.js` or `./ejs.min.js`. Alternately, you can compile it yourself by cloning
the repository and running `jake build` (or `$(npm bin)/jake build` if jake is
`./ejs.js` or `./ejs.min.js`. Alternately, you can compile it yourself by cloning
the repository and running `jake build` (or `$(npm bin)/jake build` if jake is
not installed globally).
Include one of these files on your page, and `ejs` should be available globally.
@ -208,11 +227,11 @@ Most of EJS will work as expected; however, there are a few things to note:
```javascript
var str = "Hello <%= include('file', {person: 'John'}); %>",
fn = ejs.compile(str, {client: true});
fn(data, null, function(path, d){ // IncludeCallback
// path -> 'file'
// d -> {person: 'John'}
// Put your code here
// Put your code here
// Return the contents of file as a string
}); // returns rendered string
```
@ -235,5 +254,3 @@ Licensed under the Apache License, Version 2.0
- - -
EJS Embedded JavaScript templates copyright 2112
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';
/**
* @file Embedded JavaScript templating engine.
* @file Embedded JavaScript templating engine. {@link http://ejs.co}
* @author Matthew Eernisse <mde@fleegix.org>
* @author Tiancheng "Timothy" Gu <timothygu99@gmail.com>
* @project EJS
@ -44,19 +44,23 @@
* @public
*/
var fs = require('fs')
, utils = require('./utils')
, scopeOptionWarned = false
, _VERSION_STRING = require('../package.json').version
, _DEFAULT_DELIMITER = '%'
, _DEFAULT_LOCALS_NAME = 'locals'
, _REGEX_STRING = '(<%%|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)'
, _OPTS = [ 'cache', 'filename', 'delimiter', 'scope', 'context'
, 'debug', 'compileDebug', 'client', '_with', 'rmWhitespace'
, 'strict', 'localsName'
]
, _TRAILING_SEMCOL = /;\s*$/
, _BOM = /^\uFEFF/;
var fs = require('fs');
var path = require('path');
var utils = require('./utils');
var scopeOptionWarned = false;
var _VERSION_STRING = require('../package.json').version;
var _DEFAULT_DELIMITER = '%';
var _DEFAULT_LOCALS_NAME = 'locals';
var _NAME = 'ejs';
var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';
var _OPTS = ['delimiter', 'scope', 'context', 'debug', 'compileDebug',
'client', '_with', 'rmWhitespace', 'strict', 'filename'];
// 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
@ -68,10 +72,19 @@ var fs = require('fs')
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.
*
* 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`.
*
* @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
* specified path.
*
* @param {String} name specified path
* @param {String} filename parent file path
* @param {String} name specified path
* @param {String} filename parent file path
* @param {Boolean} isDir parent file path whether is directory
* @return {String}
*/
exports.resolveInclude = function(name, filename) {
var path = require('path')
, dirname = path.dirname
, extname = path.extname
, resolve = path.resolve
, includePath = resolve(dirname(filename), name)
, ext = extname(name);
exports.resolveInclude = function(name, filename, isDir) {
var dirname = path.dirname;
var extname = path.extname;
var resolve = path.resolve;
var includePath = resolve(isDir ? filename : dirname(filename), name);
var ext = extname(name);
if (!ext) {
includePath += '.ejs';
}
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
* read from cache (if enabled), and cache the template if needed.
@ -121,35 +154,70 @@ exports.resolveInclude = function(name, filename) {
*/
function handleCache(options, template) {
var fn
, path = options.filename
, hasTemplate = arguments.length > 1;
var func;
var filename = options.filename;
var hasTemplate = arguments.length > 1;
if (options.cache) {
if (!path) {
if (!filename) {
throw new Error('cache option requires a filename');
}
fn = exports.cache.get(path);
if (fn) {
return fn;
func = exports.cache.get(filename);
if (func) {
return func;
}
if (!hasTemplate) {
template = fs.readFileSync(path).toString().replace(_BOM, '');
template = fileLoader(filename).toString().replace(_BOM, '');
}
}
else if (!hasTemplate) {
// istanbul ignore if: should not happen at all
if (!path) {
if (!filename) {
throw new Error('Internal EJS error: no file name or template '
+ '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) {
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) {
var opts = utils.shallowCopy({}, options);
if (!opts.filename) {
throw new Error('`include` requires the \'filename\' option.');
}
opts.filename = exports.resolveInclude(path, opts.filename);
opts.filename = getIncludePath(path, opts);
return handleCache(opts);
}
@ -180,24 +245,24 @@ function includeFile(path, options) {
* @memberof module:ejs-internal
* @param {String} path path for the specified file
* @param {Options} options compilation options
* @return {String}
* @return {Object}
* @static
*/
function includeSource(path, options) {
var opts = utils.shallowCopy({}, options)
, includePath
, template;
if (!opts.filename) {
throw new Error('`include` requires the \'filename\' option.');
}
includePath = exports.resolveInclude(path, opts.filename);
template = fs.readFileSync(includePath).toString().replace(_BOM, '');
var opts = utils.shallowCopy({}, options);
var includePath;
var template;
includePath = getIncludePath(path, opts);
template = fileLoader(includePath).toString().replace(_BOM, '');
opts.filename = includePath;
var templ = new Template(template, opts);
templ.generateSource();
return templ.source;
return {
source: templ.source,
filename: includePath,
template: template
};
}
/**
@ -213,11 +278,11 @@ function includeSource(path, options) {
* @static
*/
function rethrow(err, str, filename, lineno){
var lines = str.split('\n')
, start = Math.max(lineno - 3, 0)
, end = Math.min(lines.length, lineno + 3);
function rethrow(err, str, flnm, lineno, esc){
var lines = str.split('\n');
var start = Math.max(lineno - 3, 0);
var end = Math.min(lines.length, lineno + 3);
var filename = esc(flnm); // eslint-disable-line
// Error context
var context = lines.slice(start, end).map(function (line, i){
var curr = i + start + 1;
@ -237,24 +302,8 @@ function rethrow(err, str, filename, lineno){
throw err;
}
/**
* Copy properties in data object that are recognized as options to an
* options object.
*
* This is used for compatibility with earlier versions of EJS and Express.js.
*
* @memberof module:ejs-internal
* @param {Object} data data object
* @param {Options} opts options object
* @static
*/
function cpOptsInData(data, opts) {
_OPTS.forEach(function (p) {
if (typeof data[p] != 'undefined') {
opts[p] = data[p];
}
});
function stripSemi(str){
return str.replace(/;(\s*$)/, '$1');
}
/**
@ -302,15 +351,14 @@ exports.compile = function compile(template, opts) {
* @public
*/
exports.render = function (template, data, opts) {
data = data || {};
opts = opts || {};
var fn;
exports.render = function (template, d, o) {
var data = d || {};
var opts = o || {};
// No options object -- if there are optiony names
// in the data, copy them to options
if (arguments.length == 2) {
cpOptsInData(data, opts);
utils.shallowCopyFromList(opts, data, _OPTS);
}
return handleCache(opts, template)(data);
@ -330,37 +378,38 @@ exports.render = function (template, data, opts) {
*/
exports.renderFile = function () {
var args = Array.prototype.slice.call(arguments)
, path = args.shift()
, cb = args.pop()
, data = args.shift() || {}
, opts = args.pop() || {}
, result;
var filename = arguments[0];
var cb = arguments[arguments.length - 1];
var opts = {filename: filename};
var data;
// Don't pollute passed in opts obj with new vals
opts = utils.shallowCopy({}, opts);
if (arguments.length > 2) {
data = arguments[1];
// No options object -- if there are optiony names
// in the data, copy them to options
if (arguments.length == 3) {
// Express 4
if (data.settings && data.settings['view options']) {
cpOptsInData(data.settings['view options'], opts);
// No options object -- if there are optiony names
// in the data, copy them to options
if (arguments.length === 3) {
// Express 4
if (data.settings && data.settings['view options']) {
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 {
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 {
result = handleCache(opts)(data);
opts.filename = filename;
}
catch(err) {
return cb(err);
else {
data = {};
}
return cb(null, result);
return tryHandleCache(opts, data, cb);
};
/**
@ -391,6 +440,7 @@ function Template(text, opts) {
options.context = opts.context;
options.cache = opts.cache || false;
options.rmWhitespace = opts.rmWhitespace;
options.root = opts.root;
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
if (options.strict) {
@ -406,39 +456,28 @@ function Template(text, opts) {
}
Template.modes = {
EVAL: 'eval'
, ESCAPED: 'escaped'
, RAW: 'raw'
, COMMENT: 'comment'
, LITERAL: 'literal'
EVAL: 'eval',
ESCAPED: 'escaped',
RAW: 'raw',
COMMENT: 'comment',
LITERAL: 'literal'
};
Template.prototype = {
createRegex: function () {
var str = _REGEX_STRING
, delim = utils.escapeRegExpChars(this.opts.delimiter);
var str = _REGEX_STRING;
var delim = utils.escapeRegExpChars(this.opts.delimiter);
str = str.replace(/%/g, delim);
return new RegExp(str);
}
},
, compile: function () {
var src
, fn
, opts = this.opts
, prepended = ''
, appended = ''
, escape = 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, '_%>');
compile: function () {
var src;
var fn;
var opts = this.opts;
var prepended = '';
var appended = '';
var escapeFn = opts.escapeFunction;
if (!this.source) {
this.generateSource();
@ -459,7 +498,7 @@ Template.prototype = {
+ 'try {' + '\n'
+ this.source
+ '} catch (e) {' + '\n'
+ ' rethrow(e, __lines, __filename, __line);' + '\n'
+ ' rethrow(e, __lines, __filename, __line, escapeFn);' + '\n'
+ '}' + '\n';
}
else {
@ -471,7 +510,7 @@ Template.prototype = {
}
if (opts.client) {
src = 'escape = escape || ' + escape.toString() + ';' + '\n' + src;
src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src;
if (opts.compileDebug) {
src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
}
@ -482,7 +521,7 @@ Template.prototype = {
}
try {
fn = new Function(opts.localsName + ', escape, include, rethrow', src);
fn = new Function(opts.localsName + ', escapeFn, include, rethrow', src);
}
catch(e) {
// istanbul ignore else
@ -490,7 +529,9 @@ Template.prototype = {
if (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;
}
@ -511,28 +552,38 @@ Template.prototype = {
}
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;
return returnedFn;
}
},
, generateSource: function () {
var self = this
, matches = this.parseTemplateText()
, d = this.opts.delimiter;
generateSource: function () {
var opts = this.opts;
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 (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) {
var opening
, closing
, include
, includeOpts
, includeSrc;
var opening;
var closing;
var include;
var includeOpts;
var includeObj;
var includeSrc;
// If this is an opening tag, check for closing tags
// FIXME: May end up with some false positives here
// Better to store modes as k/v with '<' + delimiter as key
@ -550,9 +601,23 @@ Template.prototype = {
// Must be in EVAL or RAW mode
if (opening && (opening == '<' + d || opening == '<' + d + '-' || opening == '<' + d + '_')) {
includeOpts = utils.shallowCopy({}, self.opts);
includeSrc = includeSource(include[1], includeOpts);
includeSrc = ' ; (function(){' + '\n' + includeSrc +
' ; })()' + '\n';
includeObj = includeSource(include[1], includeOpts);
if (self.opts.compileDebug) {
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.dependencies.push(exports.resolveInclude(include[1],
includeOpts.filename));
@ -563,19 +628,17 @@ Template.prototype = {
});
}
}
},
, parseTemplateText: function () {
var str = this.templateText
, pat = this.regex
, result = pat.exec(str)
, arr = []
, firstPos
, lastPos;
parseTemplateText: function () {
var str = this.templateText;
var pat = this.regex;
var result = pat.exec(str);
var arr = [];
var firstPos;
while (result) {
firstPos = result.index;
lastPos = pat.lastIndex;
if (firstPos !== 0) {
arr.push(str.substring(0, firstPos));
@ -592,12 +655,12 @@ Template.prototype = {
}
return arr;
}
},
, scanLine: function (line) {
var self = this
, d = this.opts.delimiter
, newLineCount = 0;
scanLine: function (line) {
var self = this;
var d = this.opts.delimiter;
var newLineCount = 0;
function _addOutput() {
if (self.truncate) {
@ -606,13 +669,12 @@ Template.prototype = {
// after the tag that the truncation mode replaces
// Handle Win / Unix / old Mac linebreaks -- do the \r\n
// combo first in the regex-or
line = line.replace(/^(?:\r\n|\r|\n)/, '')
line = line.replace(/^(?:\r\n|\r|\n)/, '');
self.truncate = false;
}
else if (self.opts.rmWhitespace) {
// Gotta be more careful here.
// .replace(/^(\s*)\n/, '$1') might be more appropriate here but as
// rmWhitespace already removes trailing spaces anyway so meh.
// rmWhitespace has already removed trailing spaces, just need
// to remove linebreaks
line = line.replace(/^\n/, '');
}
if (!line) {
@ -635,73 +697,75 @@ Template.prototype = {
newLineCount = (line.split('\n').length - 1);
switch (line) {
case '<' + d:
case '<' + d + '_':
this.mode = Template.modes.EVAL;
break;
case '<' + d + '=':
this.mode = Template.modes.ESCAPED;
break;
case '<' + d + '-':
this.mode = Template.modes.RAW;
break;
case '<' + d + '#':
this.mode = Template.modes.COMMENT;
break;
case '<' + d + d:
this.mode = Template.modes.LITERAL;
this.source += ' ; __append("' + line.replace('<' + d + d, '<' + d) + '")' + '\n';
break;
case d + '>':
case '-' + d + '>':
case '_' + d + '>':
if (this.mode == Template.modes.LITERAL) {
_addOutput();
}
case '<' + d:
case '<' + d + '_':
this.mode = Template.modes.EVAL;
break;
case '<' + d + '=':
this.mode = Template.modes.ESCAPED;
break;
case '<' + d + '-':
this.mode = Template.modes.RAW;
break;
case '<' + d + '#':
this.mode = Template.modes.COMMENT;
break;
case '<' + d + d:
this.mode = Template.modes.LITERAL;
this.source += ' ; __append("' + line.replace('<' + d + d, '<' + d) + '")' + '\n';
break;
case d + d + '>':
this.mode = Template.modes.LITERAL;
this.source += ' ; __append("' + line.replace(d + d + '>', d + '>') + '")' + '\n';
break;
case d + '>':
case '-' + d + '>':
case '_' + d + '>':
if (this.mode == Template.modes.LITERAL) {
_addOutput();
}
this.mode = null;
this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;
break;
default:
this.mode = null;
this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;
break;
default:
// 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.
switch (this.mode) {
case Template.modes.EVAL:
case Template.modes.ESCAPED:
case Template.modes.RAW:
if (line.lastIndexOf('//') > line.lastIndexOf('\n')) {
line += '\n';
}
switch (this.mode) {
case Template.modes.EVAL:
case Template.modes.ESCAPED:
case Template.modes.RAW:
if (line.lastIndexOf('//') > line.lastIndexOf('\n')) {
line += '\n';
}
switch (this.mode) {
}
switch (this.mode) {
// Just executing code
case Template.modes.EVAL:
this.source += ' ; ' + line + '\n';
break;
case Template.modes.EVAL:
this.source += ' ; ' + line + '\n';
break;
// Exec, esc, and output
case Template.modes.ESCAPED:
this.source += ' ; __append(escape(' +
line.replace(_TRAILING_SEMCOL, '').trim() + '))' + '\n';
break;
case Template.modes.ESCAPED:
this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n';
break;
// Exec and output
case Template.modes.RAW:
this.source += ' ; __append(' +
line.replace(_TRAILING_SEMCOL, '').trim() + ')' + '\n';
break;
case Template.modes.COMMENT:
case Template.modes.RAW:
this.source += ' ; __append(' + stripSemi(line) + ')' + '\n';
break;
case Template.modes.COMMENT:
// Do nothing
break;
break;
// Literal <%% mode, append as raw output
case Template.modes.LITERAL:
_addOutput();
break;
}
}
// In string mode, just add the output
else {
case Template.modes.LITERAL:
_addOutput();
break;
}
}
// In string mode, just add the output
else {
_addOutput();
}
}
if (self.opts.compileDebug && newLineCount) {
@ -711,9 +775,17 @@ Template.prototype = {
}
};
/*
* Export the internal function for escaping XML so people
* can use for manual escaping if needed
/**
* Escape characters reserved in XML.
*
* 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;
@ -731,14 +803,14 @@ exports.__express = exports.renderFile;
// Add require support
/* istanbul ignore else */
if (require.extensions) {
require.extensions['.ejs'] = function (module, filename) {
filename = filename || /* istanbul ignore next */ module.filename;
require.extensions['.ejs'] = function (module, flnm) {
var filename = flnm || /* istanbul ignore next */ module.filename;
var options = {
filename: filename
, client: true
}
, template = fs.readFileSync(filename).toString()
, fn = exports.compile(template, options);
filename: filename,
client: true
};
var template = fileLoader(filename).toString();
var fn = exports.compile(template, options);
module._compile('module.exports = ' + fn.toString() + ';', filename);
};
}
@ -753,6 +825,16 @@ if (require.extensions) {
exports.VERSION = _VERSION_STRING;
/**
* Name for detection of EJS.
*
* @readonly
* @type {String}
* @public
*/
exports.name = _NAME;
/* istanbul ignore if */
if (typeof window != 'undefined') {
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 = {
'&': '&amp;'
, '<': '&lt;'
, '>': '&gt;'
, '"': '&#34;'
, "'": '&#39;'
}
, _MATCH_HTML = /[&<>\'"]/g;
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&#34;',
"'": '&#39;'
};
var _MATCH_HTML = /[&<>\'"]/g;
function encode_char(c) {
return _ENCODE_HTML_RULES[c] || c;
};
}
/**
* Stringified version of constants used by {@link module:utils.escapeXML}.
@ -98,11 +98,13 @@ exports.escapeXML = function (markup) {
.replace(_MATCH_HTML, encode_char);
};
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} from Source object
@ -118,6 +120,28 @@ exports.shallowCopy = function (to, from) {
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
* sort.
@ -138,4 +162,3 @@ exports.cache = {
this._data = {};
}
};

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

@ -1,42 +1,49 @@
{
"_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@*",
"_id": "ejs@2.4.2",
"_from": "ejs@2.5.6",
"_id": "ejs@2.5.6",
"_inCache": true,
"_installable": true,
"_location": "/ejs",
"_nodeVersion": "4.4.4",
"_nodeVersion": "6.9.1",
"_npmOperationalInternal": {
"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": {
"email": "mde@fleegix.org",
"name": "mde"
},
"_npmVersion": "2.15.1",
"_npmVersion": "3.10.8",
"_phantomChildren": {},
"_requested": {
"name": "ejs",
"raw": "ejs@*",
"rawSpec": "*",
"raw": "ejs@2.5.6",
"rawSpec": "2.5.6",
"scope": null,
"spec": "*",
"type": "range"
"spec": "2.5.6",
"type": "version"
},
"_requiredBy": [
"/"
],
"_resolved": "http://registry.npmjs.org/ejs/-/ejs-2.4.2.tgz",
"_shasum": "7057eb4812958fb731841cd9ca353343efe597b1",
"_resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.6.tgz",
"_shasum": "479636bfa3fe3b1debd52087f0acb204b4f19c88",
"_shrinkwrap": null,
"_spec": "ejs@*",
"_where": "/Users/martin/newdev/Rinser",
"_spec": "ejs@2.5.6",
"_where": "/home/martind2000/dev/Rinser",
"author": {
"email": "mde@fleegix.org",
"name": "Matthew Eernisse",
@ -47,47 +54,44 @@
},
"contributors": [
{
"name": "Timothy Gu",
"email": "timothygu99@gmail.com",
"name": "Timothy Gu",
"url": "https://timothygu.github.io"
}
],
"dependencies": {},
"description": "Embedded JavaScript templates",
"devDependencies": {
"browserify": "^8.0.3",
"istanbul": "~0.3.5",
"browserify": "^13.0.1",
"eslint": "^3.0.0",
"git-directory-deploy": "^1.5.1",
"istanbul": "~0.4.3",
"jake": "^8.0.0",
"jsdoc": "^3.3.0-beta1",
"lru-cache": "^2.5.0",
"mocha": "^2.1.0",
"rimraf": "^2.2.8",
"uglify-js": "^2.4.16"
"jsdoc": "^3.4.0",
"lru-cache": "^4.0.1",
"mocha": "^3.0.2",
"uglify-js": "^2.6.2"
},
"directories": {},
"dist": {
"shasum": "7057eb4812958fb731841cd9ca353343efe597b1",
"tarball": "https://registry.npmjs.org/ejs/-/ejs-2.4.2.tgz"
"shasum": "479636bfa3fe3b1debd52087f0acb204b4f19c88",
"tarball": "https://registry.npmjs.org/ejs/-/ejs-2.5.6.tgz"
},
"engines": {
"node": ">=0.10.0"
},
"homepage": "https://github.com/mde/ejs",
"keywords": [
"ejs",
"template",
"engine",
"template"
"ejs"
],
"license": "Apache-2.0",
"main": "./lib/ejs.js",
"maintainers": [
{
"name": "tjholowaychuk",
"email": "tj@vision-media.ca"
},
{
"name": "mde",
"email": "mde@fleegix.org"
"email": "mde@fleegix.org",
"name": "mde"
}
],
"name": "ejs",
@ -99,10 +103,10 @@
},
"scripts": {
"coverage": "istanbul cover node_modules/mocha/bin/_mocha",
"devdoc": "rimraf out && jsdoc -p -c jsdoc.json lib/* docs/jsdoc/*",
"doc": "rimraf out && jsdoc -c jsdoc.json lib/* docs/jsdoc/*",
"sample": "npm install express && node sample/index.js",
"devdoc": "jake doc[dev]",
"doc": "jake doc",
"lint": "eslint \"**/*.js\" Jakefile",
"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 */
/* eslint-env node, mocha */
/**
* Module dependencies.
*/
var ejs = require('..')
, fs = require('fs')
, read = fs.readFileSync
, assert = require('assert')
, path = require('path')
, LRU = require('lru-cache');
var ejs = require('..');
var fs = require('fs');
var read = fs.readFileSync;
var assert = require('assert');
var path = require('path');
var LRU = require('lru-cache');
try {
fs.mkdirSync(__dirname + '/tmp');
@ -103,10 +104,28 @@ suite('ejs.compile(str, options)', function () {
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 () {
var fn
, str
, preFn;
var fn;
var str;
var preFn;
fn = ejs.compile('<p><%= foo %></p>', {client: true});
str = fn.toString();
if (!process.env.running_under_istanbul) {
@ -116,9 +135,9 @@ suite('ejs.compile(str, options)', function () {
});
test('support client mode without locals', function () {
var fn
, str
, preFn;
var fn;
var str;
var preFn;
fn = ejs.compile('<p><%= "foo" %></p>', {client: true});
str = fn.toString();
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 () {
var fn = ejs.compile('<p><%= "foo" %></p>', {
client: true
, compileDebug: false
});
client: true,
compileDebug: false
});
// There could be a `rethrow` in the function declaration
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 () {
var customEscape
, fn
, str;
var customEscape;
var fn;
var str;
customEscape = function customEscape(str) {
return !str ? '' : str.toUpperCase();
};
@ -157,10 +166,31 @@ suite('ejs.compile(str, options)', function () {
str = fn.toString();
if (!process.env.running_under_istanbul) {
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 () {
@ -224,10 +254,10 @@ suite('ejs.render(str, data, opts)', function () {
});
test('support caching', function () {
var file = __dirname + '/tmp/render.ejs'
, options = {cache: true, filename: file}
, out = ejs.render('<p>Old</p>', {}, options)
, expected = '<p>Old</p>';
var file = __dirname + '/tmp/render.ejs';
var options = {cache: true, filename: file};
var out = ejs.render('<p>Old</p>', {}, options);
var expected = '<p>Old</p>';
assert.equal(out, expected);
// Assert no change, still in cache
out = ejs.render('<p>New</p>', {}, options);
@ -235,11 +265,11 @@ suite('ejs.render(str, data, opts)', function () {
});
test('support LRU caching', function () {
var oldCache = ejs.cache
, file = __dirname + '/tmp/render.ejs'
, options = {cache: true, filename: file}
, out
, expected = '<p>Old</p>';
var oldCache = ejs.cache;
var file = __dirname + '/tmp/render.ejs';
var options = {cache: true, filename: file};
var out;
var expected = '<p>Old</p>';
// Switch to LRU
ejs.cache = LRU();
@ -255,8 +285,8 @@ suite('ejs.render(str, data, opts)', function () {
});
test('opts.context', function () {
var ctxt = {foo: 'FOO'}
, out = ejs.render('<%= this.foo %>', {}, {context: ctxt});
var ctxt = {foo: 'FOO'};
var out = ejs.render('<%= this.foo %>', {}, {context: ctxt});
assert.equal(out, ctxt.foo);
});
});
@ -273,8 +303,8 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
});
test('accept locals', function(done) {
var data = {name: 'fonebone'}
, options = {delimiter: '$'};
var data = {name: 'fonebone'};
var options = {delimiter: '$'};
ejs.renderFile('test/fixtures/user.ejs', data, options, function(err, html) {
if (err) {
return done(err);
@ -285,11 +315,10 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
});
test('accept locals without using with() {}', function(done) {
var data = {name: 'fonebone'}
, options = {delimiter: '$', _with: false}
, doneCount = 0;
ejs.renderFile('test/fixtures/user-no-with.ejs', data, options,
function(err, html) {
var data = {name: 'fonebone'};
var options = {delimiter: '$', _with: false};
var doneCount = 0;
ejs.renderFile('test/fixtures/user-no-with.ejs', data, options, function(err, html) {
if (err) {
if (doneCount === 2) {
return;
@ -319,9 +348,9 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
});
test('not catch err thrown by callback', function(done) {
var data = {name: 'fonebone'}
, options = {delimiter: '$'}
, counter = 0;
var data = {name: 'fonebone'};
var options = {delimiter: '$'};
var counter = 0;
var d = require('domain').create();
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
// deprecated :D"
process.nextTick(function () {
ejs.renderFile('test/fixtures/user.ejs', data, options,
function(err) {
ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) {
counter++;
if (err) {
assert.notEqual(err.message, 'Exception in callback');
@ -350,9 +378,9 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
});
test('support caching', function (done) {
var expected = '<p>Old</p>'
, file = __dirname + '/tmp/renderFile.ejs'
, options = {cache: true};
var expected = '<p>Old</p>';
var file = __dirname + '/tmp/renderFile.ejs';
var options = {cache: true};
fs.writeFileSync(file, '<p>Old</p>');
ejs.renderFile(file, {}, options, function (err, out) {
@ -375,8 +403,7 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
test('opts.context', function (done) {
var ctxt = {foo: 'FOO'};
ejs.renderFile('test/fixtures/with-context.ejs', {},
{context: ctxt}, function(err, html) {
ejs.renderFile('test/fixtures/with-context.ejs', {}, {context: ctxt}, function(err, html) {
if (err) {
return done(err);
}
@ -389,10 +416,10 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
suite('cache specific', function () {
test('`clearCache` work properly', function () {
var expected = '<p>Old</p>'
, file = __dirname + '/tmp/clearCache.ejs'
, options = {cache: true, filename: file}
, out = ejs.render('<p>Old</p>', {}, options);
var expected = '<p>Old</p>';
var file = __dirname + '/tmp/clearCache.ejs';
var options = {cache: true, filename: file};
var out = ejs.render('<p>Old</p>', {}, options);
assert.equal(out, expected);
ejs.clearCache();
@ -403,11 +430,11 @@ suite('cache specific', function () {
});
test('`clearCache` work properly, LRU', function () {
var expected = '<p>Old</p>'
, oldCache = ejs.cache
, file = __dirname + '/tmp/clearCache.ejs'
, options = {cache: true, filename: file}
, out;
var expected = '<p>Old</p>';
var oldCache = ejs.cache;
var file = __dirname + '/tmp/clearCache.ejs';
var options = {cache: true, filename: file};
var out;
ejs.cache = LRU();
@ -422,11 +449,11 @@ suite('cache specific', function () {
});
test('LRU with cache-size 1', function () {
var oldCache = ejs.cache
, options
, out
, expected
, file;
var oldCache = ejs.cache;
var options;
var out;
var expected;
var file;
ejs.cache = LRU(1);
@ -472,6 +499,11 @@ 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 () {
assert.equal(ejs.render('<%= name %>', {name: '&nbsp;<script>'}),
'&amp;nbsp;&lt;script&gt;');
@ -501,6 +533,11 @@ 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 () {
assert.equal(ejs.render('<%- name %>', {name: '<script>'}),
'<script>');
@ -551,13 +588,13 @@ suite('-%>', function () {
});
test('works with unix style', function () {
var content = "<ul><% -%>\n"
+ "<% users.forEach(function(user){ -%>\n"
+ "<li><%= user.name -%></li>\n"
+ "<% }) -%>\n"
+ "</ul><% -%>\n";
var content = '<ul><% -%>\n'
+ '<% users.forEach(function(user){ -%>\n'
+ '<li><%= user.name -%></li>\n'
+ '<% }) -%>\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;
fn = ejs.compile(content);
assert.equal(fn({users: users}),
@ -565,13 +602,13 @@ suite('-%>', function () {
});
test('works with windows style', function () {
var content = "<ul><% -%>\r\n"
+ "<% users.forEach(function(user){ -%>\r\n"
+ "<li><%= user.name -%></li>\r\n"
+ "<% }) -%>\r\n"
+ "</ul><% -%>\r\n";
var content = '<ul><% -%>\r\n'
+ '<% users.forEach(function(user){ -%>\r\n'
+ '<li><%= user.name -%></li>\r\n'
+ '<% }) -%>\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;
fn = ejs.compile(content);
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 () {
test('slurps spaces and tabs', function () {
assert.equal(ejs.render(fixture('space-and-tab-slurp.ejs'), {users: users}),
@ -656,8 +702,8 @@ suite('exceptions', function () {
var unhook = null;
test('log JS source when debug is set', function (done) {
var out = ''
, needToExit = false;
var out = '';
var needToExit = false;
unhook = hook_stdio(process.stdout, function (str) {
out += str;
if (needToExit) {
@ -672,6 +718,22 @@ suite('exceptions', function () {
});
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() {
if (!unhook) {
return;
@ -727,6 +789,13 @@ suite('include()', function () {
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 () {
var file = 'test/fixtures/menu.ejs';
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 () {
var file = 'test/fixtures/menu_var.ejs',
includePath = 'includes/menu-item';
var file = 'test/fixtures/menu_var.ejs';
var includePath = 'includes/menu-item';
assert.equal(ejs.render(fixture('menu.ejs'), {pets: users, varPath: includePath}, {filename: file}),
fixture('menu.html'));
});
@ -747,12 +816,12 @@ suite('include()', function () {
});
test('pass compileDebug to include', function () {
var file = 'test/fixtures/include.ejs'
, fn;
var file = 'test/fixtures/include.ejs';
var fn;
fn = ejs.compile(fixture('include.ejs'), {
filename: file
, delimiter: '@'
, compileDebug: false
filename: file,
delimiter: '@',
compileDebug: false
});
try {
// Render without a required variable reference
@ -768,9 +837,9 @@ suite('include()', function () {
test('is dynamic', function () {
fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_cache.ejs'
, options = {filename: file}
, out = ejs.compile(fixture('include_cache.ejs'), options);
var file = 'test/fixtures/include_cache.ejs';
var options = {filename: file};
var out = ejs.compile(fixture('include_cache.ejs'), options);
assert.equal(out(), '<p>Old</p>\n');
fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>New</p>');
@ -779,10 +848,10 @@ suite('include()', function () {
test('support caching', function () {
fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_cache.ejs'
, options = {cache: true, filename: file}
, out = ejs.render(fixture('include_cache.ejs'), {}, options)
, expected = fixture('include_cache.html');
var file = 'test/fixtures/include_cache.ejs';
var options = {cache: true, filename: file};
var out = ejs.render(fixture('include_cache.ejs'), {}, options);
var expected = fixture('include_cache.html');
assert.equal(out, expected);
out = ejs.render(fixture('include_cache.ejs'), {}, options);
// No change, still in cache
@ -830,8 +899,8 @@ suite('preprocessor include', function () {
});
test('tracks dependency correctly', function () {
var file = 'test/fixtures/menu_preprocessor.ejs'
, fn = ejs.compile(fixture('menu_preprocessor.ejs'), {filename: file});
var file = 'test/fixtures/menu_preprocessor.ejs';
var fn = ejs.compile(fixture('menu_preprocessor.ejs'), {filename: file});
assert(fn.dependencies.length);
});
@ -842,12 +911,12 @@ suite('preprocessor include', function () {
});
test('pass compileDebug to include', function () {
var file = 'test/fixtures/include_preprocessor.ejs'
, fn;
var file = 'test/fixtures/include_preprocessor.ejs';
var fn;
fn = ejs.compile(fixture('include_preprocessor.ejs'), {
filename: file
, delimiter: '@'
, compileDebug: false
filename: file,
delimiter: '@',
compileDebug: false
});
try {
// Render without a required variable reference
@ -863,9 +932,9 @@ suite('preprocessor include', function () {
test('is static', function () {
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_preprocessor_cache.ejs'
, options = {filename: file}
, out = ejs.compile(fixture('include_preprocessor_cache.ejs'), options);
var file = 'test/fixtures/include_preprocessor_cache.ejs';
var options = {filename: file};
var out = ejs.compile(fixture('include_preprocessor_cache.ejs'), options);
assert.equal(out(), '<p>Old</p>\n');
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>');
@ -874,16 +943,25 @@ suite('preprocessor include', function () {
test('support caching', function () {
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>');
var file = 'test/fixtures/include_preprocessor_cache.ejs'
, options = {cache: true, filename: file}
, out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options)
, expected = fixture('include_preprocessor_cache.html');
var file = 'test/fixtures/include_preprocessor_cache.ejs';
var options = {cache: true, filename: file};
var out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options);
var expected = fixture('include_preprocessor_cache.html');
assert.equal(out, expected);
fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>');
out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options);
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 () {
@ -897,12 +975,31 @@ suite('require', function () {
// Only works with inline/preprocessor includes
test('allow ejs templates to be required as node modules', function () {
var file = 'test/fixtures/include_preprocessor.ejs'
, template = require(__dirname + '/fixtures/menu_preprocessor.ejs');
if (!process.env.running_under_istanbul) {
assert.equal(template({filename: file, pets: users}),
var file = 'test/fixtures/include_preprocessor.ejs';
var template = require(__dirname + '/fixtures/menu_preprocessor.ejs');
if (!process.env.running_under_istanbul) {
assert.equal(template({filename: file, pets: users}),
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 () {
test('doesn\'t throw any errors', function () {
var stderr = hook_stdio(process.stderr, noop)
, stdout = hook_stdio(process.stdout, noop);
var stderr = hook_stdio(process.stderr, noop);
var stdout = hook_stdio(process.stdout, noop);
try {
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
<% data = "test"; -%>
<% var data = "test"; -%>
BBB
<%= qdata %>
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
===================

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

@ -37,13 +37,16 @@ $ npm install express
## 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
* [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
* [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).

View File

@ -28,6 +28,7 @@ var deprecate = require('depd')('express');
var flatten = require('array-flatten');
var merge = require('utils-merge');
var resolve = require('path').resolve;
var setPrototypeOf = require('setprototypeof')
var slice = Array.prototype.slice;
/**
@ -94,10 +95,10 @@ app.defaultConfiguration = function defaultConfiguration() {
}
// inherit protos
this.request.__proto__ = parent.request;
this.response.__proto__ = parent.response;
this.engines.__proto__ = parent.engines;
this.settings.__proto__ = parent.settings;
setPrototypeOf(this.request, parent.request)
setPrototypeOf(this.response, parent.response)
setPrototypeOf(this.engines, parent.engines)
setPrototypeOf(this.settings, parent.settings)
});
// setup locals
@ -227,8 +228,8 @@ app.use = function use(fn) {
router.use(path, function mounted_app(req, res, next) {
var orig = req.app;
fn.handle(req, res, function (err) {
req.__proto__ = orig.request;
res.__proto__ = orig.response;
setPrototypeOf(req, orig.request)
setPrototypeOf(res, orig.response)
next(err);
});
});
@ -261,9 +262,9 @@ app.route = function route(path) {
*
* By default will `require()` the engine based on the
* 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,
* 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, proto, false);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
// expose the prototype that will get set on requests
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();
return app;
}

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

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

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

@ -14,17 +14,18 @@
var contentDisposition = require('content-disposition');
var deprecate = require('depd')('express');
var encodeUrl = require('encodeurl');
var escapeHtml = require('escape-html');
var http = require('http');
var isAbsolute = require('./utils').isAbsolute;
var onFinished = require('on-finished');
var path = require('path');
var statuses = require('statuses')
var merge = require('utils-merge');
var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes;
var setCharset = require('./utils').setCharset;
var statusCodes = http.STATUS_CODES;
var cookie = require('cookie');
var send = require('send');
var extname = path.extname;
@ -34,11 +35,17 @@ var vary = require('vary');
/**
* Response prototype.
* @public
*/
var res = module.exports = {
__proto__: http.ServerResponse.prototype
};
var res = Object.create(http.ServerResponse.prototype)
/**
* Module exports.
* @public
*/
module.exports = res
/**
* Module variables.
@ -128,7 +135,7 @@ res.send = function send(body) {
deprecate('res.send(status): Use res.sendStatus(status) instead');
this.statusCode = chunk;
chunk = statusCodes[chunk];
chunk = statuses[chunk]
}
switch (typeof chunk) {
@ -189,7 +196,7 @@ res.send = function send(body) {
if (req.fresh) this.statusCode = 304;
// strip irrelevant headers
if (204 == this.statusCode || 304 == this.statusCode) {
if (204 === this.statusCode || 304 === this.statusCode) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
this.removeHeader('Transfer-Encoding');
@ -239,7 +246,7 @@ res.json = function json(obj) {
var app = this.app;
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = JSON.stringify(val, replacer, spaces);
var body = stringify(val, replacer, spaces);
// content-type
if (!this.get('Content-Type')) {
@ -281,7 +288,7 @@ res.jsonp = function jsonp(obj) {
var app = this.app;
var replacer = app.get('json replacer');
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')];
// content-type
@ -333,7 +340,7 @@ res.jsonp = function jsonp(obj) {
*/
res.sendStatus = function sendStatus(statusCode) {
var body = statusCodes[statusCode] || String(statusCode);
var body = statuses[statusCode] || String(statusCode)
this.statusCode = statusCode;
this.type('txt');
@ -710,9 +717,14 @@ res.header = function header(field, val) {
: String(val);
// add charset to content-type
if (field.toLowerCase() === 'content-type' && !charsetRegExp.test(value)) {
var charset = mime.charsets.lookup(value.split(';')[0]);
if (charset) value += '; charset=' + charset.toLowerCase();
if (field.toLowerCase() === 'content-type') {
if (Array.isArray(value)) {
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);
@ -740,7 +752,7 @@ res.get = function(field){
* Clear cookie `name`.
*
* @param {String} name
* @param {Object} options
* @param {Object} [options]
* @return {ServerResponse} for chaining
* @public
*/
@ -770,7 +782,7 @@ res.clearCookie = function clearCookie(name, options) {
*
* @param {String} name
* @param {String|Object} value
* @param {Options} options
* @param {Object} [options]
* @return {ServerResponse} for chaining
* @public
*/
@ -832,8 +844,7 @@ res.location = function location(url) {
}
// set location
this.set('Location', loc);
return this;
return this.set('Location', encodeUrl(loc));
};
/**
@ -871,18 +882,17 @@ res.redirect = function redirect(url) {
}
// Set location header
this.location(address);
address = this.get('Location');
address = this.location(address).get('Location');
// Support text/{plain,html} by default
this.format({
text: function(){
body = statusCodes[status] + '. Redirecting to ' + encodeURI(address);
body = statuses[status] + '. Redirecting to ' + address
},
html: function(){
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(){
@ -1051,3 +1061,16 @@ function sendfile(res, file, options, callback) {
// pipe
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 flatten = require('array-flatten');
var parseUrl = require('parseurl');
var setPrototypeOf = require('setprototypeof')
/**
* Module variables.
@ -47,7 +48,7 @@ var proto = module.exports = function(options) {
}
// mixin Router class functions
router.__proto__ = proto;
setPrototypeOf(router, proto)
router.params = {};
router._params = [];
@ -119,7 +120,7 @@ proto.param = function param(name, fn) {
// ensure we end up with a
// middleware function
if ('function' != typeof fn) {
if ('function' !== typeof 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);
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 protohost = getProtohost(req.url) || ''
var removed = '';
var slashAdded = false;
var paramcalled = {};
@ -193,6 +191,12 @@ proto.handle = function handle(req, res, out) {
removed = '';
}
// signal to exit router
if (layerError === 'router') {
setImmediate(done, null)
return
}
// no more matching layers
if (idx >= stack.length) {
setImmediate(done, layerError);
@ -282,18 +286,19 @@ proto.handle = function handle(req, res, out) {
}
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) {
// 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);
removed = layerPath;
req.url = protohost + req.url.substr(protohost.length + removed.length);
// Ensure leading slash
if (!fqdn && req.url[0] !== '/') {
if (!protohost && req.url[0] !== '/') {
req.url = '/' + req.url;
slashAdded = true;
}
@ -351,11 +356,6 @@ proto.process_params = function process_params(layer, called, req, res, done) {
paramIndex = 0;
key = keys[i++];
if (!key) {
return done();
}
name = key.name;
paramVal = req.params[name];
paramCallbacks = params[name];
@ -459,7 +459,7 @@ proto.use = function use(fn) {
}
// add the middleware
debug('use %s %s', path, fn.name || '<anonymous>');
debug('use %o %s', path, fn.name || '<anonymous>')
var layer = new Layer(path, {
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
function gettype(obj) {
var type = typeof obj;
@ -609,7 +626,7 @@ function restore(fn, obj) {
vals[i] = obj[props[i]];
}
return function(err){
return function () {
// restore vals
for (var i = 0; i < props.length; i++) {
obj[props[i]] = vals[i];

View File

@ -44,7 +44,7 @@ function Route(path) {
this.path = path;
this.stack = [];
debug('new %s', path);
debug('new %o', path)
// route handlers for various http methods
this.methods = {};
@ -112,10 +112,16 @@ Route.prototype.dispatch = function dispatch(req, res, done) {
next();
function next(err) {
// signal to exit route
if (err && err === 'route') {
return done();
}
// signal to exit router
if (err && err === 'router') {
return done(err)
}
var layer = stack[idx++];
if (!layer) {
return done(err);
@ -196,7 +202,7 @@ methods.forEach(function(method){
throw new Error(msg);
}
debug('%s %s', method, this.path);
debug('%s %o', method, this.path)
var layer = Layer('/', {}, handle);
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){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
if ('/' === path[0]) 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
};
/**
@ -142,7 +142,7 @@ function acceptParams(str, index) {
for (var i = 1; i < parts.length; ++i) {
var pms = parts[i].split(/ *= */);
if ('q' == pms[0]) {
if ('q' === pms[0]) {
ret.quality = parseFloat(pms[1]);
} else {
ret.params[pms[0]] = pms[1];
@ -283,7 +283,6 @@ exports.setCharset = function setCharset(type, charset) {
function parseExtendedQueryString(str) {
return qs.parse(str, {
allowDots: false,
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]) {
// 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

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

@ -1,39 +1,63 @@
{
"_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",
"_id": "express@4.13.4",
"_from": "express@4.15.3",
"_id": "express@4.15.3",
"_inCache": true,
"_installable": true,
"_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": {
"email": "doug@somethingdoug.com",
"name": "dougwilson"
},
"_npmVersion": "1.4.28",
"_npmVersion": "4.2.0",
"_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": {
"name": "express",
"raw": "express@^4.13.4",
"rawSpec": "^4.13.4",
"raw": "express@4.15.3",
"rawSpec": "4.15.3",
"scope": null,
"spec": ">=4.13.4 <5.0.0",
"type": "range"
"spec": "4.15.3",
"type": "version"
},
"_requiredBy": [
"/"
],
"_resolved": "http://registry.npmjs.org/express/-/express-4.13.4.tgz",
"_shasum": "3c0b76f3c77590c8345739061ec0bd3ba067ec24",
"_resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz",
"_shasum": "bab65d0f03aa80c358408972fc700f916944b662",
"_shrinkwrap": null,
"_spec": "express@^4.13.4",
"_where": "/Users/martin/newdev/Rinser",
"_spec": "express@4.15.3",
"_where": "/home/martind2000/dev/Rinser",
"author": {
"email": "tj@vision-media.ca",
"name": "TJ Holowaychuk"
@ -43,114 +67,130 @@
},
"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": {
"accepts": "~1.2.12",
"accepts": "~1.3.3",
"array-flatten": "1.1.1",
"content-disposition": "0.5.1",
"content-type": "~1.0.1",
"cookie": "0.1.5",
"content-disposition": "0.5.2",
"content-type": "~1.0.2",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "~2.2.0",
"debug": "2.6.7",
"depd": "~1.1.0",
"encodeurl": "~1.0.1",
"escape-html": "~1.0.3",
"etag": "~1.7.0",
"finalhandler": "0.4.1",
"fresh": "0.3.0",
"etag": "~1.8.0",
"finalhandler": "~1.0.3",
"fresh": "0.5.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.1",
"path-to-regexp": "0.1.7",
"proxy-addr": "~1.0.10",
"qs": "4.0.0",
"range-parser": "~1.0.3",
"send": "0.13.1",
"serve-static": "~1.10.2",
"type-is": "~1.6.6",
"proxy-addr": "~1.1.4",
"qs": "6.4.0",
"range-parser": "~1.2.0",
"send": "0.15.3",
"serve-static": "1.12.3",
"setprototypeof": "1.0.3",
"statuses": "~1.3.1",
"type-is": "~1.6.15",
"utils-merge": "1.0.0",
"vary": "~1.0.1"
"vary": "~1.1.1"
},
"description": "Fast, unopinionated, minimalist web framework",
"devDependencies": {
"after": "0.8.1",
"body-parser": "~1.14.2",
"after": "0.8.2",
"body-parser": "1.17.1",
"connect-redis": "~2.4.1",
"cookie-parser": "~1.4.1",
"cookie-parser": "~1.4.3",
"cookie-session": "~1.2.0",
"ejs": "2.3.4",
"express-session": "~1.13.0",
"istanbul": "0.4.2",
"jade": "~1.11.0",
"marked": "0.3.5",
"method-override": "~2.3.5",
"mocha": "2.3.4",
"morgan": "~1.6.1",
"multiparty": "~4.1.2",
"should": "7.1.1",
"supertest": "1.1.0",
"vhost": "~3.0.1"
"ejs": "2.5.6",
"express-session": "1.15.2",
"hbs": "4.0.1",
"istanbul": "0.4.5",
"marked": "0.3.6",
"method-override": "2.3.8",
"mocha": "3.4.1",
"morgan": "1.8.1",
"multiparty": "4.1.3",
"pbkdf2-password": "1.2.1",
"should": "11.2.1",
"supertest": "1.2.0",
"vhost": "~3.0.2"
},
"directories": {},
"dist": {
"shasum": "3c0b76f3c77590c8345739061ec0bd3ba067ec24",
"tarball": "https://registry.npmjs.org/express/-/express-4.13.4.tgz"
"shasum": "bab65d0f03aa80c358408972fc700f916944b662",
"tarball": "https://registry.npmjs.org/express/-/express-4.15.3.tgz"
},
"engines": {
"node": ">= 0.10.0"
},
"files": [
"History.md",
"LICENSE",
"History.md",
"Readme.md",
"index.js",
"lib/"
],
"gitHead": "193bed2649c55c1fd362e46cd4702c773f3e7434",
"homepage": "https://github.com/expressjs/express",
"gitHead": "6da454c7fb37e68ed65ffe0371aa688b89f5bd6e",
"homepage": "http://expressjs.com/",
"keywords": [
"api",
"app",
"express",
"framework",
"sinatra",
"web",
"rest",
"restful",
"router",
"sinatra",
"web"
"app",
"api"
],
"license": "MIT",
"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",
@ -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-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": [
"#USER",
"/",
"/cheerio",
"/gulp-jshint"
"/async",
"/gulp-jshint",
"/gulp-uglify",
"/hipchat-notifier",
"/mailgun-js/async",
"/requestretry",
"/rss-braider"
],
"_resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"_shasum": "78203a4d1c328ae1d86dca6460e369b57f4055ae",

80
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)
[![Dependencies](https://david-dm.org/rKQED/rss-braider.svg?branch=master)](https://david-dm.org/KQED/rss-braider)
## 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
@ -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
```
## Test
`npm test`
```
npm install
npm test
```
## Examples
```
$ cd examples
$ 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
```js

View File

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

View File

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

View File

@ -2,37 +2,47 @@
"_args": [
[
{
"name": null,
"raw": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"rawSpec": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"name": "rss-braider",
"raw": "rss-braider",
"rawSpec": "",
"scope": null,
"spec": "http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"type": "git"
"spec": "latest",
"type": "tag"
},
"/home/martind2000/dev/Rinser"
]
],
"_from": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"_id": "rss-braider@1.0.0",
"_from": "rss-braider@latest",
"_id": "rss-braider@1.2.0",
"_inCache": true,
"_installable": true,
"_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": {},
"_requested": {
"name": null,
"raw": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"rawSpec": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"name": "rss-braider",
"raw": "rss-braider",
"rawSpec": "",
"scope": null,
"spec": "http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"type": "git"
"spec": "latest",
"type": "tag"
},
"_requiredBy": [
"#DEV:/"
"/"
],
"_resolved": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git#1559131d2e2d73076307667d260f0e173e64c8c3",
"_shasum": "4ca2443627b6ffd330424d220aa10635f4f8cb8c",
"_resolved": "https://registry.npmjs.org/rss-braider/-/rss-braider-1.2.0.tgz",
"_shasum": "ce134542a39b643b916b95dd08a10dbfd7f1c28b",
"_shrinkwrap": null,
"_spec": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git",
"_spec": "rss-braider",
"_where": "/home/martind2000/dev/Rinser",
"author": {
"email": "kgebhardt@kqed.org",
@ -43,20 +53,26 @@
"url": "http://github.com/KQED/rss-braider/issues"
},
"dependencies": {
"async": "^1.2.1",
"async": "^2.1.5",
"bunyan": "^1.4.0",
"feedparser": "^1.1.3",
"include-folder": "^0.9.0",
"lodash": "^3.9.3",
"feedparser": "^2.1.0",
"include-folder": "^1.0.0",
"lodash": "^4.17.4",
"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.",
"devDependencies": {
"mockdate": "^1.0.3",
"mockdate": "^2.0.1",
"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": [
"rss",
"braider",
@ -77,16 +93,21 @@
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"email": "kgebhardt23@gmail.com",
"name": "rv-kip"
}
],
"name": "rss-braider",
"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",
"readmeFilename": "README.md",
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+http://gitlab.silvrtree.co.uk/martind2000/rss-braider.git"
"url": "git+https://github.com/KQED/rss-braider.git"
},
"scripts": {
"test": "tape test"
},
"version": "1.0.0"
"version": "1.2.0"
}

View File

@ -1,15 +1,14 @@
<?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/">
<?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/">
<channel>
<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>
<generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item>
<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;
&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>
<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>
<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>
<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>
@ -27,7 +26,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10220517</guid>
<dc:creator><![CDATA[Sarah Hotchkiss]]></dc:creator>
@ -55,7 +54,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10231062</guid>
<dc:creator><![CDATA[KQED Arts]]></dc:creator>
@ -72,7 +71,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10230592</guid>
<dc:creator><![CDATA[Kevin L. Jones]]></dc:creator>
@ -91,7 +90,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10189953</guid>
<dc:creator><![CDATA[Michele Carlson]]></dc:creator>

View File

@ -1,14 +1,13 @@
<?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/">
<?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/">
<channel>
<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>
<generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid>
<dc:creator><![CDATA[Michael Fox]]></dc:creator>
@ -56,7 +55,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238611</guid>
<dc:creator><![CDATA[The Do List]]></dc:creator>
@ -98,7 +97,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238600</guid>
<dc:creator><![CDATA[Siouxsie Oki]]></dc:creator>
@ -118,7 +117,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10238610</guid>
<dc:creator><![CDATA[Siouxsie Oki]]></dc:creator>
@ -139,7 +138,7 @@
</item>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10218934</guid>
<dc:creator><![CDATA[Brian Eder]]></dc:creator>

View File

@ -1,5 +1,4 @@
<?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">
<?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">
<channel>
<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>

View File

@ -1,5 +1,4 @@
<?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">
<?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">
<channel>
<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>

View File

@ -1,14 +1,13 @@
<?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/">
<?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/">
<channel>
<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>
<generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid>
<dc:creator><![CDATA[Michael Fox]]></dc:creator>

View File

@ -1,14 +1,13 @@
<?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/">
<?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/">
<channel>
<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>
<generator>rss-braider</generator>
<lastBuildDate>Wed, 31 Dec 2014 00:00:01 GMT</lastBuildDate>
<item>
<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>
<guid isPermaLink="false">http://ww2.kqed.org/arts/?p=10222283</guid>
<dc:creator><![CDATA[Michael Fox]]></dc:creator>

View File

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

View File

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

File diff suppressed because one or more lines are too long