-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
155 lines (128 loc) · 4.64 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
'use strict';
const through = require('through2');
const gUtil = require('gulp-util');
const split = require('split');
const XRegExp = require('xregexp');
const hasProperties = require('has-properties');
const replaceAll = require("replaceall");
const PluginError = gUtil.PluginError;
let map;
// consts
const PLUGIN_NAME = 'gulp-replace-frommap';
module.exports = function (options) {
// through2.obj(fn) is a convinient wrapper around through2({ objectMode: true }, fn)
// through2 --> stream funcs made consistent *magic*
return through.obj(function (file, encoding, callback) {
// if file is empty, just do nothing
if(file.isNull()) {
return callback(null, file);
}
// if all options are not present
// TODO do some advanced error checks
if(!hasProperties(options, ['map', 'before', 'after'])) {
this.emit('error', new PluginError(PLUGIN_NAME, 'Options are required!'));
return callback();
} else {
// load mapper to be used later on
map = require(options.map);
}
if(file.isStream()) {
this.emit('error', new PluginError(PLUGIN_NAME, 'Streaming is not yet supported!'));
return callback();
}
if(file.isBuffer()) {
let filecontents = file.contents.toString(encoding),
replaced = '',
current,
// remnesance of good ol' c pointer days
pointer = getIndexOf(filecontents, options.before),
stringSuperman;
// if pointer is -1 then exit straight away with the file as is
if(pointer === -1) {
return callback(null, file);
}
// put the contents before the first match into the replaced
// go on until the last before match
while(1) {
pointer = getIndexOf(filecontents, options.before);
// exit loop only if invalid pointer
if(pointer === -1) {
break;
}
current = filecontents.substr(0, pointer);
replaced = replaced.concat(current);
stringSuperman = getStringWithLength(filecontents, options.before);
replaced = replaced.concat(stringSuperman.textValue);
pointer += stringSuperman.textLength;
// cut them matched text loose
filecontents = filecontents.substr(pointer);
// now look for after
pointer = getIndexOf(filecontents, options.after);
// if after is not found throw a error and exit
if(pointer === -1) {
this.emit('error', new PluginError(PLUGIN_NAME, 'After is not found'));
return callback();
}
// use the after match's position to get the tokenized and replaced string
current = filecontents.substr(0, pointer);
// cut off the match part from the original source
filecontents = filecontents.substr(pointer);
current = replaceTextWithMap(current, map);
replaced = replaced.concat(current);
// now attach the after text
stringSuperman = getStringWithLength(filecontents, options.after);
replaced = replaced.concat(stringSuperman.textValue);
pointer = stringSuperman.textLength;
// cut them matched text loose
filecontents = filecontents.substr(pointer);
}
// write off the last chunk which is left
replaced = replaced.concat(filecontents);
// write data back to the file
file.contents = Buffer.from(replaced, encoding);
return callback(null, file);
}
});
// get index of using regex or normal way -1 other wise
function getIndexOf(string, find) {
// if regex then do it regex way
if(XRegExp.isRegExp(find)) {
return string.search(find);
} else {
// normal way
return string.indexOf(find);
}
}
// get the full matched regex string or the string itself along with it's length
function getStringWithLength(string, find) {
let obj;
// if regex then do it regex way
if(XRegExp.isRegExp(find)) {
obj = {
textValue: XRegExp.replace(string, find, '$1', 'one'),
textLength: XRegExp.match(string, /class/g, 'one')
.length
};
return;
} else {
obj = {
textValue: find,
textLength: find.length
};
}
return obj;
}
// replace until all of the map has are exhausted
function replaceTextWithMap(string, map) {
// break the words into tokens using _ and words themselves
const tokens = XRegExp.match(string, /([a-z0-9_]+)/ig);
// for all the tokens replace it in string
for(let token of tokens) {
// try to replace only if the key exists in the map else skip over
if(map.hasOwnProperty(token)) {
string = replaceAll(token, map[token], string);
}
}
return string;
}
};