grunt-yabs
Version:
Collection of tools for grunt release workflows.
195 lines (180 loc) • 5.31 kB
JavaScript
/*
* applause
*
* Copyright (c) 2015 outaTiME
* Licensed under the MIT license.
* https://github.com/outaTiME/applause/blob/master/LICENSE-MIT
*/
// dependencies
var _ = require('lodash');
var plugins = require('./plugins');
// took from MDN
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
var escapeRegExp = function (string) {
return string.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
};
var getPatterns = function (applause) {
var opts = applause.options;
// shallow patterns
var patterns = _.chain(opts.patterns)
.clone()
.compact()
.filter(function (pattern) {
return !_.isEmpty(pattern);
})
.value();
// backward compatibility
var variables = opts.variables;
if (!_.isEmpty(variables)) {
patterns.push({
json: variables
});
}
// process
for (var i = patterns.length - 1; i >= 0; i -= 1) {
var pattern = patterns[i];
// plugins
plugins.forEach(function (plugin) {
if (plugin.match(pattern, opts) === true) {
plugin.transform(pattern, opts, function (items) {
if (items instanceof Error) {
throw items;
} else {
// store transformed pattern in context
pattern = items;
}
});
} else {
// plugin doesn't apply
}
});
// convert to array
if (!_.isArray(pattern)) {
pattern = [pattern];
}
// link with pattern with original source
pattern.forEach(function (pattern) {
pattern.source = patterns[i];
});
// attach index
Array.prototype.splice.apply(patterns, [i, 1].concat(pattern));
}
if (opts.preserveOrder !== true) {
// only sort non regex patterns (prevents replace issues like head, header)
patterns.sort(function (a, b) {
var x = a.match;
var y = b.match;
if (_.isString(x) && _.isString(y)) {
return y.length - x.length;
} else if (_.isString(x)) {
return -1;
}
return 1;
});
}
return patterns;
};
// applause
var Applause = function (opts) {
this.options = _.defaults(opts, {
patterns: [],
prefix: opts.usePrefix === false ? '' : '@@',
usePrefix: true,
preservePrefix: false,
delimiter: '.',
preserveOrder: false
});
};
Applause.prototype.replace = function (content, process) {
var opts = this.options;
// prevent null
content = content || '';
// prepare patterns
var patterns = getPatterns(this);
var detail = [];
var total_count = 0;
// iterate over each pattern and make replacement
patterns.forEach(function (pattern, i) {
// filter empty patterns
var match = pattern.match;
// support replace flag too
var replacement = pattern.replacement;
if (replacement === undefined || replacement === null) {
replacement = pattern.replace;
}
var source = pattern.source;
var expression = false;
// match check
if (match !== undefined && match !== null) {
if (_.isRegExp(match)) {
expression = true;
} else if (_.isString(match)) {
if (match.length > 0) {
match = new RegExp(opts.prefix + escapeRegExp(match), 'g');
} else {
// empty match
return;
}
} else {
throw new Error('Unsupported match type (RegExp or String expected).');
}
} else {
throw new Error('Match attribute expected in pattern definition.');
}
// replacement check
if (replacement !== undefined && replacement !== null) {
if (!_.isFunction(replacement)) {
if (!_.isString(replacement)) {
// transform object to string
replacement = JSON.stringify(replacement);
}
if (expression === false) {
// escape dollar sequences in easy mode
replacement = replacement.replace(/\$/g, '$$$');
// preserve prefix
if (opts.preservePrefix === true) {
replacement = opts.prefix + replacement;
}
}
} else {
// replace using function return value
replacement = function () {
var args = Array.prototype.slice.call(arguments);
return pattern.replacement.apply(this, args.concat(process || []));
};
}
} else {
throw new Error('Replacement attribute expected in pattern definition.');
}
// replace logic
var count = (content.match(match) || []).length;
if (count > 0) {
// update content
content = content.replace(match, replacement);
// save detail data
detail.push({
match: match,
replacement: replacement,
source: pattern.source,
count: count
});
total_count += count;
}
});
// FIXME: always return detailed result
if (detail.length === 0) {
content = false;
}
return {
content: content,
detail: detail,
count: total_count
};
};
// static
Applause.create = function (opts) {
return new Applause(opts);
};
Applause.version = require('../package.json').version;
// expose
module.exports = Applause;