diff --git a/index.js b/index.js
index c282fd3..520813e 100644
--- a/index.js
+++ b/index.js
@@ -1,11 +1,11 @@
var through2 = require('through2');
-var _ = require('lodash');
+var defaults = require('lodash.defaults');
var StringDecoder = require('string_decoder').StringDecoder;
var debug = require('debug')('htmlprep');
var Parser = require('./lib/parser');
exports = module.exports = function(options) {
- options = _.defaults(options, {
+ options = defaults(options, {
attrPrefix: null,
buildType: 'debug',
liveReload: false, // Should livereload script be injected
diff --git a/lib/attribute-modifier.js b/lib/attribute-modifier.js
index a7bd3cb..1fd0003 100644
--- a/lib/attribute-modifier.js
+++ b/lib/attribute-modifier.js
@@ -1,4 +1,11 @@
-var _ = require('lodash');
+var defaults = require('lodash.defaults');
+var trimStart = require('lodash.trimstart');
+var trimEnd = require('lodash.trimend');
+var includes = require('lodash.includes');
+var isEmpty = require('lodash.isempty');
+var isString = require('lodash.isstring');
+var toArray = require('lodash.toarray');
+var has = require('lodash.has');
var minimatch = require('minimatch');
var absoluteUrlRe = /^http[s]?:\/\//i;
@@ -7,7 +14,12 @@ var linkRelAttributeUrlValues = ['alternate', 'help', 'license', 'next', 'prev',
var inlineCssUrlRe = /url\(["']?(.*?)["']?\)/;
module.exports = function(options) {
- _.defaults(options, {noPathPrefixPatterns: []});
+ defaults(options, {noPathPrefixPatterns: []});
+
+ // Strip leading slashes from prefix patterns
+ options.noPathPrefixPatterns = options.noPathPrefixPatterns.map(function(pattern) {
+ return trimStart(pattern, '/');
+ });
var customAttribute = require('./custom-attribute')(options.attrPrefix);
@@ -40,26 +52,26 @@ module.exports = function(options) {
// If the tagName is not a standard src attribute tag, leave it alone.
// For example angular2 does this:
- if (srcAttr === 'src' && !_.includes(validSrcAttributeTags, tagName)) {
+ if (srcAttr === 'src' && !includes(validSrcAttributeTags, tagName)) {
return;
}
var attrValue = attribs[srcAttr];
// If the attribute is empty, nothing to do.
- if (_.isEmpty(attrValue)) return;
+ if (isEmpty(attrValue)) return;
// If the assetPathPrefix option is specified and there is not a data-src-keep
// attribute, then prepend the assetPathPrefix. This is generally to repoint
// static assets to an absolute CDN url.
- if (options.assetPathPrefix && !_.has(attribs, customAttribute('src-keep'))) {
+ if (options.assetPathPrefix && !has(attribs, customAttribute('src-keep'))) {
attrValue = prependAssetPath(attrValue);
}
// If the fingerprint option is specified and the data-fingerprint custom attribute
// is declared on the tag, then append the fingerprint to the assetPath
var fingerprintAttr = customAttribute('fingerprint');
- if (options.fingerprint && _.has(attribs, fingerprintAttr)) {
+ if (options.fingerprint && has(attribs, fingerprintAttr)) {
attrValue += (attrValue.indexOf('?') === -1 ? '?' : '&');
attrValue += options.fingerprintQuery + '=' + options.fingerprint;
attribs[fingerprintAttr] = null;
@@ -72,7 +84,7 @@ module.exports = function(options) {
// Don't prepend the asset path to any path that matches
// one of the noAssetPathPrefixes patterns.
for (var i = 0; i < options.noPathPrefixPatterns.length; i++) {
- if (minimatch(assetPath, options.noPathPrefixPatterns[i])) {
+ if (minimatch(trimStart(assetPath, '/'), options.noPathPrefixPatterns[i])) {
return assetPath;
}
}
@@ -98,7 +110,7 @@ module.exports = function(options) {
assetPath = stripExtraLeadingSlash(assetPath);
if (assetPath.substr(0, 2) === '//') return assetPath;
- if (assetPath[0] !== '/' && !_.isEmpty(options.pathFromRoot)) {
+ if (assetPath[0] !== '/' && !isEmpty(options.pathFromRoot)) {
return slashJoin(options.assetPathPrefix, options.pathFromRoot, assetPath);
}
return slashJoin(options.assetPathPrefix, assetPath);
@@ -141,7 +153,7 @@ module.exports = function(options) {
}
function replaceBaseUrlPlaceholder(attr) {
- if (!_.isString(attr) || !options.baseUrlPlaceholder || !options.baseUrl) return attr;
+ if (!isString(attr) || !options.baseUrlPlaceholder || !options.baseUrl) return attr;
return attr.replace(options.baseUrlPlaceholder, options.baseUrl);
}
@@ -151,16 +163,16 @@ module.exports = function(options) {
}
function slashJoin() {
- var items = _.toArray(arguments);
+ var items = toArray(arguments);
for (var i = 0; i < items.length; i++) {
// Trim off any leading slashes
if (i > 0) {
- items[i] = _.trimStart(items[i], '/');
+ items[i] = trimStart(items[i], '/');
}
// If not the last item and the rightmost character is a slash
if (i < items.length - 1) {
- items[i] = _.trimEnd(items[i], '/');
+ items[i] = trimEnd(items[i], '/');
}
}
@@ -169,14 +181,14 @@ module.exports = function(options) {
function isResourceLink(tagName, attribs) {
if (tagName !== 'link' || !attribs.href) return false;
- if (!_.includes(linkRelAttributeUrlValues, attribs.rel)) return true;
+ if (!includes(linkRelAttributeUrlValues, attribs.rel)) return true;
return false;
}
// Determine if this tag has an href attribute that is a hyperlink to another URL.
function isHrefHyperlink(tagName, attribs) {
if (tagName === 'a' && attribs.href) return true;
- if (tagName === 'link' && _.includes(linkRelAttributeUrlValues, attribs.rel)) return true;
+ if (tagName === 'link' && includes(linkRelAttributeUrlValues, attribs.rel)) return true;
return false;
}
diff --git a/lib/parser.js b/lib/parser.js
index 22c0fcb..ac3b21b 100644
--- a/lib/parser.js
+++ b/lib/parser.js
@@ -2,7 +2,12 @@ var EventEmitter = require('events').EventEmitter;
var HtmlParser = require('../htmlparser2').Parser;
var debug = require('debug')('htmlprep');
var util = require('util');
-var _ = require('lodash');
+var includes = require('lodash.includes');
+var isEmpty = require('lodash.isempty');
+var isString = require('lodash.isstring');
+var isNull = require('lodash.isnull');
+var forEach = require('lodash.foreach');
+var has = require('lodash.has');
var glob = require('glob');
var singletonTags = ['link', 'meta', 'param', 'source', 'area', 'base', 'br', 'col',
@@ -92,14 +97,14 @@ Parser.prototype.onOpenTag = function(tagName, attribs) {
debug('open %s', tagName);
// Trim all the attribs
- _.each(attribs, function(value, key) {
+ forEach(attribs, function(value, key) {
attribs[key] = value.trim();
});
// Content variation block
if (this._removing !== true) {
// var contentVariation = attribs[customAttrs.contentVariation];
- // if (_.isEmpty(contentVariation) === false) {
+ // if (isEmpty(contentVariation) === false) {
// if (context.tagMatch) throw new Error("Invalid nesting of content-variation element");
// context.variations[variation] = new VariationBuffer();
@@ -108,14 +113,14 @@ Parser.prototype.onOpenTag = function(tagName, attribs) {
// }
// var variationName = attribs[customAttrs.variation];
- // if (_.isEmpty(variationName) === false) {
+ // if (isEmpty(variationName) === false) {
// if (this._tagMatch)
// throw new Error("Invalid nesting of variation element");
// if (options.variation && variationName !== options.variation) {
// // Check if we have content for this variation name.
// var substituteContent = context.contentVariations[variationName];
- // if (_.isEmpty(substituteContent) === false) {
+ // if (isEmpty(substituteContent) === false) {
// // Write the substitute content.
// context.writeOutput(substituteContent);
// context.startTagMatch(name, true);
@@ -129,12 +134,12 @@ Parser.prototype.onOpenTag = function(tagName, attribs) {
// If there is a data-placeholder attribute, replace the tag
// with the new contents.
var placeholder = attribs[this._customAttribute('placeholder')];
- if (_.isEmpty(placeholder) === false) {
+ if (isEmpty(placeholder) === false) {
attribs[this._customAttribute('placeholder')] = null;
this._output.push(buildTag(tagName, attribs));
- if (_.isEmpty(this._options.inject[placeholder]) === false) {
+ if (isEmpty(this._options.inject[placeholder]) === false) {
debug('injecting block %s', placeholder);
this._output.push(this._options.inject[placeholder]);
}
@@ -153,13 +158,13 @@ Parser.prototype.onOpenTag = function(tagName, attribs) {
if (this._removing === true) return;
// If the data-strip attribute is present, remove this tag and everything within it.
- if (_.has(attribs, this._customAttribute('strip'))) {
+ if (has(attribs, this._customAttribute('strip'))) {
this.startTagMatch(tagName, true);
return;
}
var buildType = attribs[this._customAttribute('build')];
- if (_.isEmpty(buildType) === false) {
+ if (isEmpty(buildType) === false) {
this.startTagMatch(tagName, buildType !== this._options.buildType);
if (this._removing === true) return;
@@ -202,7 +207,7 @@ Parser.prototype.onOpenTag = function(tagName, attribs) {
this._attributeModifier(tagName, attribs);
- this._output.push(buildTag(tagName, attribs, _.includes(singletonTags, tagName)));
+ this._output.push(buildTag(tagName, attribs, includes(singletonTags, tagName)));
};
Parser.prototype.onCloseTag = function(tagName) {
@@ -218,7 +223,7 @@ Parser.prototype.onCloseTag = function(tagName) {
if (removing === true) return;
// Don't close singleton tags
- if (_.includes(singletonTags, tagName)) return;
+ if (includes(singletonTags, tagName)) return;
// Special blocks appended to the head
if (tagName === 'head') {
@@ -267,7 +272,7 @@ Parser.prototype.popTag = function(tagName) {
Parser.prototype.appendFingerprintQuery = function(attribs) {
var assetPath = attribs.src;
- if (_.isEmpty(assetPath)) return;
+ if (isEmpty(assetPath)) return;
// If this is an embedded url, leave it be.
if (assetPath.slice(0, 5) === 'data:') return;
@@ -301,10 +306,10 @@ Parser.prototype.expandGlobPattern = function(tagName, pattern, attribs) {
function buildTag(name, attribs, selfClosing) {
var tag = '<' + name;
- _.each(attribs, function(attrValue, key) {
- if (_.isString(attrValue) && attrValue.length === 0) {
+ forEach(attribs, function(attrValue, key) {
+ if (isString(attrValue) && attrValue.length === 0) {
tag += ' ' + key;
- } else if (_.isNull(attrValue) === false) {
+ } else if (isNull(attrValue) === false) {
tag += ' ' + key + '="' + attrValue + '"';
}
});
diff --git a/package.json b/package.json
index a3b17d5..8523571 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "htmlprep",
- "version": "1.5.2",
+ "version": "1.5.3",
"description": "High-performance streaming HTML pre-processor designed to run in middleware.",
"main": "index.js",
"scripts": {
@@ -33,7 +33,17 @@
"domutils": "^1.5.1",
"entities": "^1.1.1",
"glob": "^7.0.3",
- "lodash": "^4.6.1",
+ "lodash.defaults": "^4.0.1",
+ "lodash.foreach": "^4.2.0",
+ "lodash.has": "^4.3.1",
+ "lodash.includes": "^4.1.2",
+ "lodash.isempty": "^4.2.1",
+ "lodash.isfunction": "^3.0.8",
+ "lodash.isnull": "^3.0.0",
+ "lodash.isstring": "^4.0.1",
+ "lodash.toarray": "^4.2.4",
+ "lodash.trimend": "^4.3.0",
+ "lodash.trimstart": "^4.3.0",
"minimatch": "^3.0.0",
"readable-stream": "^2.0.5",
"through2": "^2.0.1"
diff --git a/test/attributes.js b/test/attributes.js
index 9b11c9e..3fc6417 100644
--- a/test/attributes.js
+++ b/test/attributes.js
@@ -306,6 +306,22 @@ describe('htmlprep attributes', function() {
});
});
+ it('noAssetPathPrefixes handles non-leading slash', function(done) {
+ var html = '';
+
+ var opts = {
+ noPathPrefixPatterns: ['/img/*.jpg'],
+ assetPathPrefix: '//cdn.net/'
+ };
+
+ run(html, opts, function(err, output) {
+ if (err) return done(err);
+
+ assert.equal(output, html);
+ done();
+ });
+ });
+
it('trims leading whitespace from attributes', function(done) {
var html = ' Home';
diff --git a/test/run.js b/test/run.js
index db78772..9185c73 100644
--- a/test/run.js
+++ b/test/run.js
@@ -1,9 +1,9 @@
-var _ = require('lodash');
+var isFunction = require('lodash.isfunction');
var htmlprep = require('../');
var stream = require('stream');
module.exports = function(html, options, callback) {
- if (_.isFunction(options)) {
+ if (isFunction(options)) {
callback = options;
options = {};
}