123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- 'use strict';
- var escapeRegExp = require('escape-string-regexp');
- var objectAssign = require('object-assign');
- var Transform = require('readable-stream/transform');
- module.exports = function ReplaceStream(search, replace, options) {
- var tail = '';
- var totalMatches = 0;
- var isRegex = search instanceof RegExp;
- options = objectAssign({
- limit: Infinity,
- encoding: 'utf8',
- maxMatchLen: 100
- }, options);
- var replaceFn = replace;
- replaceFn = createReplaceFn(replace, isRegex);
- var match;
- if (isRegex) {
- match = matchFromRegex(search, options)
- } else {
- match = matchFromString(search, options);
- options.maxMatchLen = search.length;
- }
- function transform(buf, enc, cb) {
- var matches;
- var lastPos = 0;
- var runningMatch = '';
- var matchCount = 0;
- var rewritten = '';
- var haystack = tail + buf.toString(options.encoding);
- tail = '';
- while (totalMatches < options.limit &&
- (matches = match.exec(haystack)) !== null) {
- matchCount++;
- var before = haystack.slice(lastPos, matches.index);
- var regexMatch = matches;
- lastPos = matches.index + regexMatch[0].length;
- if (lastPos > haystack.length && regexMatch[0].length < options.maxMatchLen) {
- tail = regexMatch[0]
- } else {
- var dataToAppend = getDataToAppend(before,regexMatch);
- rewritten += dataToAppend;
- }
- }
- if (tail.length < 1)
- tail = haystack.slice(lastPos).length > options.maxMatchLen ? haystack.slice(lastPos).slice(0 - options.maxMatchLen) : haystack.slice(lastPos)
- var dataToQueue = getDataToQueue(matchCount,haystack,rewritten,lastPos);
- cb(null, dataToQueue);
- }
- function getDataToAppend(before, match) {
- var dataToAppend = before;
- totalMatches++;
- dataToAppend += isRegex ? replaceFn.apply(this, match.concat([match.index, match.input])) : replaceFn(match[0]);
- return dataToAppend;
- }
- function getDataToQueue(matchCount, haystack, rewritten, lastPos) {
- if (matchCount > 0) {
- if (haystack.length > tail.length) {
- return rewritten + haystack.slice(lastPos, haystack.length - tail.length);
- }
- return rewritten;
- }
- return haystack.slice(0, haystack.length - tail.length);
- }
- function flush(cb) {
- if (tail) {
- this.push(tail);
- }
- cb();
- }
- return new Transform({
- transform: transform,
- flush: flush
- });
- };
- function createReplaceFn(replace, isRegEx) {
- var regexReplaceFunction = function () {
- var newReplace = replace;
- // ability to us $1 with captures
- // Start at 1 and end at length - 2 to avoid the match parameter and offset
- // And string parameters
- var paramLength = arguments.length - 2;
- for (var i = 1; i < paramLength; i++) {
- newReplace = newReplace.replace(new RegExp('\\$' + i, 'g'), arguments[i] || '')
- }
- return newReplace;
- };
- if (isRegEx && !(replace instanceof Function)) {
- return regexReplaceFunction;
- }
- if (!(replace instanceof Function)) {
- return function stringReplaceFunction() {
- return replace;
- };
- }
- return replace;
- }
- function matchFromRegex(regex, options) {
- if (options.regExpOptions) {
- regex = new RegExp(regex.source, options.regExpOptions)
- }
- // If there is no global flag then there can only be one match
- if (!regex.global) {
- options.limit = 1;
- }
- return regex;
- }
- function matchFromString(s, options) {
- if (options.regExpOptions) {
- return new RegExp(escapeRegExp(s), options.regExpOptions);
- }
- return new RegExp(escapeRegExp(s), options.ignoreCase === false ? 'gm' : 'gmi');
- }
|