@stackbit/cms-core 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotator/html.d.ts +1 -0
- package/dist/annotator/html.js +54 -0
- package/dist/annotator/html.js.map +1 -0
- package/dist/annotator/index.d.ts +13 -0
- package/dist/annotator/index.js +120 -0
- package/dist/annotator/index.js.map +1 -0
- package/dist/annotator/react.d.ts +1 -0
- package/dist/annotator/react.js +53 -0
- package/dist/annotator/react.js.map +1 -0
- package/dist/consts.d.ts +27 -0
- package/dist/consts.js +43 -0
- package/dist/consts.js.map +1 -0
- package/dist/encoder.d.ts +8 -0
- package/dist/encoder.js +272 -0
- package/dist/encoder.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/stackbit/index.d.ts +4 -0
- package/dist/stackbit/index.js +54 -0
- package/dist/stackbit/index.js.map +1 -0
- package/dist/utils/index.d.ts +172 -0
- package/dist/utils/index.js +928 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +43 -0
- package/src/annotator/html.js +55 -0
- package/src/annotator/index.js +135 -0
- package/src/annotator/react.js +56 -0
- package/src/consts.js +41 -0
- package/src/encoder.js +315 -0
- package/src/index.js +13 -0
- package/src/stackbit/index.js +55 -0
- package/src/utils/index.js +997 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const fse = require('fs-extra');
|
|
4
|
+
const glob = require('glob');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
const yaml = require('js-yaml');
|
|
7
|
+
const toml = require('@iarna/toml');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
forEachPromise,
|
|
12
|
+
mapPromise,
|
|
13
|
+
reducePromise,
|
|
14
|
+
findPromise,
|
|
15
|
+
promiseAllMap,
|
|
16
|
+
copyFilesRecursively,
|
|
17
|
+
copy,
|
|
18
|
+
copyDefault,
|
|
19
|
+
mergeAtPath,
|
|
20
|
+
omitByNil,
|
|
21
|
+
rename,
|
|
22
|
+
append,
|
|
23
|
+
concat,
|
|
24
|
+
indent,
|
|
25
|
+
pascalCase,
|
|
26
|
+
readDirRec,
|
|
27
|
+
readDirRecSync,
|
|
28
|
+
readDirGlob,
|
|
29
|
+
fieldPathToString,
|
|
30
|
+
hrtimeAndPrint,
|
|
31
|
+
printHRTime,
|
|
32
|
+
forEachDeep,
|
|
33
|
+
mapDeep,
|
|
34
|
+
getFirst,
|
|
35
|
+
getFirstExistingFile,
|
|
36
|
+
parseFirstExistingFile,
|
|
37
|
+
getFirstExistingFileSync,
|
|
38
|
+
parseFirstExistingFileSync,
|
|
39
|
+
parseFile,
|
|
40
|
+
parseFileSync,
|
|
41
|
+
parseDataByFilePath,
|
|
42
|
+
outputData,
|
|
43
|
+
outputDataSync,
|
|
44
|
+
outputDataIfNeeded,
|
|
45
|
+
stringifyDataByFilePath,
|
|
46
|
+
parseMarkdownWithFrontMatter,
|
|
47
|
+
deepFreeze,
|
|
48
|
+
failFunctionWithTag,
|
|
49
|
+
assertFunctionWithFail,
|
|
50
|
+
createLogger,
|
|
51
|
+
logObject,
|
|
52
|
+
joinPathAndGlob,
|
|
53
|
+
globToArray,
|
|
54
|
+
fromPath,
|
|
55
|
+
omitDeep,
|
|
56
|
+
dataReducerSync,
|
|
57
|
+
dataReducer,
|
|
58
|
+
encodeJsx,
|
|
59
|
+
decodeJsx,
|
|
60
|
+
replaceInRange,
|
|
61
|
+
isRelevantReactData
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const INDENT = _.repeat(' ', 4);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Iterates over array items and invokes callback function for each of them.
|
|
68
|
+
* The callback must return a promise and is called with three parameters: array item,
|
|
69
|
+
* item index, array itself. Callbacks are invoked serially, such that callback for the
|
|
70
|
+
* following item will not be called until the promise returned from the previous callback
|
|
71
|
+
* is not fulfilled.
|
|
72
|
+
*
|
|
73
|
+
* @param {array} array
|
|
74
|
+
* @param {function} callback
|
|
75
|
+
* @param {object} [thisArg]
|
|
76
|
+
* @return {Promise<any>}
|
|
77
|
+
*/
|
|
78
|
+
function forEachPromise(array, callback, thisArg) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
function next(index) {
|
|
81
|
+
if (index < array.length) {
|
|
82
|
+
callback
|
|
83
|
+
.call(thisArg, array[index], index, array)
|
|
84
|
+
.then(() => {
|
|
85
|
+
next(index + 1);
|
|
86
|
+
})
|
|
87
|
+
.catch((error) => {
|
|
88
|
+
reject(error);
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
resolve();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
next(0);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function mapPromise(array, callback, thisArg) {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
let results = [];
|
|
101
|
+
|
|
102
|
+
function next(index) {
|
|
103
|
+
if (index < array.length) {
|
|
104
|
+
callback
|
|
105
|
+
.call(thisArg, array[index], index, array)
|
|
106
|
+
.then((result) => {
|
|
107
|
+
results[index] = result;
|
|
108
|
+
next(index + 1);
|
|
109
|
+
})
|
|
110
|
+
.catch((error) => {
|
|
111
|
+
reject(error);
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
resolve(results);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
next(0);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function reducePromise(array, callback, initValue, thisArg) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
function next(index, accumulator) {
|
|
125
|
+
if (index < array.length) {
|
|
126
|
+
callback
|
|
127
|
+
.call(thisArg, accumulator, array[index], index, array)
|
|
128
|
+
.then((accumulator) => {
|
|
129
|
+
next(index + 1, accumulator);
|
|
130
|
+
})
|
|
131
|
+
.catch((error) => {
|
|
132
|
+
reject(error);
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
resolve(accumulator);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
next(0, initValue);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function reduceObjectPromise(object, callback, initValue, thisArg) {
|
|
144
|
+
return new Promise((resolve, reject) => {
|
|
145
|
+
const keys = _.keys(object);
|
|
146
|
+
function next(index, accumulator) {
|
|
147
|
+
if (index < keys.length) {
|
|
148
|
+
const key = keys[index];
|
|
149
|
+
callback
|
|
150
|
+
.call(thisArg, accumulator, object[key], key, object)
|
|
151
|
+
.then((accumulator) => {
|
|
152
|
+
next(index + 1, accumulator);
|
|
153
|
+
})
|
|
154
|
+
.catch((error) => {
|
|
155
|
+
reject(error);
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
resolve(accumulator);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
next(0, initValue);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function findPromise(array, callback, thisArg) {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
function next(index) {
|
|
169
|
+
if (index < array.length) {
|
|
170
|
+
const item = array[index];
|
|
171
|
+
callback
|
|
172
|
+
.call(thisArg, item, index, array)
|
|
173
|
+
.then((result) => {
|
|
174
|
+
if (result) {
|
|
175
|
+
resolve(item);
|
|
176
|
+
} else {
|
|
177
|
+
next(index + 1);
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
.catch((error) => {
|
|
181
|
+
reject(error);
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
resolve();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
next(0);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function promiseAllMap(array, limit, interval, callback, thisArg) {
|
|
193
|
+
return new Promise((resolve, reject) => {
|
|
194
|
+
const arrayCopy = array.slice();
|
|
195
|
+
const results = [];
|
|
196
|
+
let index = 0;
|
|
197
|
+
let runCount = 0;
|
|
198
|
+
let doneCount = 0;
|
|
199
|
+
let lastRunTime = null;
|
|
200
|
+
let timeout = null;
|
|
201
|
+
limit = limit || null;
|
|
202
|
+
interval = interval || null;
|
|
203
|
+
|
|
204
|
+
function run() {
|
|
205
|
+
let idx = index;
|
|
206
|
+
if (timeout) {
|
|
207
|
+
clearTimeout(timeout);
|
|
208
|
+
timeout = null;
|
|
209
|
+
}
|
|
210
|
+
index += 1;
|
|
211
|
+
runCount += 1;
|
|
212
|
+
lastRunTime = process.hrtime();
|
|
213
|
+
Promise.resolve(callback.call(thisArg, arrayCopy[idx], idx, arrayCopy))
|
|
214
|
+
.then((result) => {
|
|
215
|
+
runCount -= 1;
|
|
216
|
+
doneCount += 1;
|
|
217
|
+
results.push(result);
|
|
218
|
+
next();
|
|
219
|
+
})
|
|
220
|
+
.catch((error) => {
|
|
221
|
+
reject(error);
|
|
222
|
+
});
|
|
223
|
+
next();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (interval) {
|
|
227
|
+
let origRun = run;
|
|
228
|
+
run = function () {
|
|
229
|
+
if (!lastRunTime) {
|
|
230
|
+
origRun();
|
|
231
|
+
} else if (!timeout) {
|
|
232
|
+
let diff = process.hrtime(lastRunTime);
|
|
233
|
+
let diffMs = diff[0] * 1000 + diff[1] / 1000000;
|
|
234
|
+
if (diffMs >= interval) {
|
|
235
|
+
origRun();
|
|
236
|
+
} else {
|
|
237
|
+
timeout = setTimeout(origRun, interval - diffMs);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (limit) {
|
|
244
|
+
let origRun = run;
|
|
245
|
+
run = function () {
|
|
246
|
+
if (runCount < limit) {
|
|
247
|
+
origRun();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function next() {
|
|
253
|
+
if (index < arrayCopy.length) {
|
|
254
|
+
run();
|
|
255
|
+
} else if (doneCount === arrayCopy.length) {
|
|
256
|
+
resolve(results);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
next();
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Recursively copies files from source to target directories.
|
|
266
|
+
* The optional "options" argument is an object with an optional "processNunjucksFile"
|
|
267
|
+
* and "filePathMap" fields.
|
|
268
|
+
*
|
|
269
|
+
* If "processNunjucksFile" function is passed, it will be invoked for every file with ".njk"
|
|
270
|
+
* extension with a filepath relative to the sourceDir as its single argument.
|
|
271
|
+
* This function should return the result of processing Nunjucks template.
|
|
272
|
+
*
|
|
273
|
+
* Files named _gitignore will be copied as .gitignore
|
|
274
|
+
*
|
|
275
|
+
* @param {string} sourceDir
|
|
276
|
+
* @param {string} targetDir
|
|
277
|
+
* @param {object} [options]
|
|
278
|
+
* @param {Function} options.processNunjucksFile Function that receives filePath
|
|
279
|
+
* relative to sourceDir and returns processed file data to be stored inside targetDir
|
|
280
|
+
* @param {object} options.filePathMap Map between source and target file paths.
|
|
281
|
+
* If mapped value is null, the file will not be copied.
|
|
282
|
+
*/
|
|
283
|
+
function copyFilesRecursively(sourceDir, targetDir, options, _internalOptions) {
|
|
284
|
+
if (!_internalOptions) {
|
|
285
|
+
_internalOptions = {
|
|
286
|
+
origSourceDir: sourceDir,
|
|
287
|
+
origTargetDir: targetDir
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
fs.readdirSync(sourceDir).forEach((fileName) => {
|
|
292
|
+
let sourceFilePath = path.join(sourceDir, fileName);
|
|
293
|
+
let targetFilePath = path.join(targetDir, fileName);
|
|
294
|
+
let fileStat = fs.statSync(sourceFilePath);
|
|
295
|
+
|
|
296
|
+
if (fileStat.isDirectory()) {
|
|
297
|
+
copyFilesRecursively(sourceFilePath, targetFilePath, options, _internalOptions);
|
|
298
|
+
} else if (fileStat.isFile()) {
|
|
299
|
+
let outputPathObject = path.parse(targetFilePath);
|
|
300
|
+
let data = null;
|
|
301
|
+
if (outputPathObject.ext === '.njk') {
|
|
302
|
+
if (!_.has(options, 'processNunjucksFile') || !_.isFunction(options.processNunjucksFile)) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`utils.copyFilesRecursively(): file (${sourceFilePath}) has '.njk' extension but processNunjucksFile function was not passed`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
let relativeSourceFilePath = path.relative(_internalOptions.origSourceDir, sourceFilePath);
|
|
308
|
+
data = options.processNunjucksFile(relativeSourceFilePath);
|
|
309
|
+
targetFilePath = path.resolve(outputPathObject.dir, outputPathObject.name);
|
|
310
|
+
}
|
|
311
|
+
let relativeTargetFilePath = path.relative(_internalOptions.origTargetDir, targetFilePath);
|
|
312
|
+
if (_.has(options, ['filePathMap', relativeTargetFilePath])) {
|
|
313
|
+
let mappedFilePath = _.get(options, ['filePathMap', relativeTargetFilePath]);
|
|
314
|
+
if (mappedFilePath === null) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
targetFilePath = path.join(_internalOptions.origTargetDir, mappedFilePath);
|
|
318
|
+
}
|
|
319
|
+
if (fileName === '_gitignore') {
|
|
320
|
+
targetFilePath = path.resolve(outputPathObject.dir, '.gitignore');
|
|
321
|
+
}
|
|
322
|
+
if (!data) {
|
|
323
|
+
fse.copySync(sourceFilePath, targetFilePath);
|
|
324
|
+
} else {
|
|
325
|
+
fse.outputFileSync(targetFilePath, data, { mode: fileStat.mode });
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
throw new Error(`utils.copyFilesRecursively(): file type is not supported: ${sourceFilePath}`);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Copies the value at a sourcePath of the sourceObject to a targetPath of the targetObject.
|
|
335
|
+
*
|
|
336
|
+
* @param {Object} sourceObject
|
|
337
|
+
* @param {String} sourcePath
|
|
338
|
+
* @param {Object} targetObject
|
|
339
|
+
* @param {String} targetPath
|
|
340
|
+
* @param {Function} [transform]
|
|
341
|
+
*/
|
|
342
|
+
function copy(sourceObject, sourcePath, targetObject, targetPath, transform) {
|
|
343
|
+
if (_.has(sourceObject, sourcePath)) {
|
|
344
|
+
let value = _.get(sourceObject, sourcePath);
|
|
345
|
+
if (transform) {
|
|
346
|
+
value = transform(value);
|
|
347
|
+
}
|
|
348
|
+
_.set(targetObject, targetPath, value);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function copyDefault(sourceObject, sourcePath, targetObject, targetPath, transform) {
|
|
353
|
+
if (!_.has(targetObject, targetPath)) {
|
|
354
|
+
copy(sourceObject, sourcePath, targetObject, targetPath, transform);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function mergeAtPath(object, path, source) {
|
|
359
|
+
// First get the existing object at path, merge it with source and then set
|
|
360
|
+
// the merged object back. This ensures that if path has number like field
|
|
361
|
+
// names while the original object has objects at this path, it will not
|
|
362
|
+
// override the object with array
|
|
363
|
+
const nodeAtPath = _.get(object, path);
|
|
364
|
+
const merged = _.merge(nodeAtPath, source);
|
|
365
|
+
return _.set(object, path, merged);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function omitByNil(object) {
|
|
369
|
+
return _.omitBy(object, _.isNil);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function rename(object, oldPath, newPath) {
|
|
373
|
+
if (_.has(object, oldPath)) {
|
|
374
|
+
_.set(object, newPath, _.get(object, oldPath));
|
|
375
|
+
oldPath = _.toPath(oldPath);
|
|
376
|
+
if (oldPath.length > 1) {
|
|
377
|
+
object = _.get(object, _.initial(oldPath));
|
|
378
|
+
}
|
|
379
|
+
delete object[_.last(oldPath)];
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function append(object, path, value) {
|
|
384
|
+
if (!_.has(object, path)) {
|
|
385
|
+
_.set(object, path, []);
|
|
386
|
+
}
|
|
387
|
+
_.get(object, path).push(value);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function concat(object, path, value) {
|
|
391
|
+
if (!_.has(object, path)) {
|
|
392
|
+
_.set(object, path, []);
|
|
393
|
+
}
|
|
394
|
+
_.set(object, path, _.get(object, path).concat(value));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function indent(str, indent, indentFirst = false) {
|
|
398
|
+
if (_.isNumber(indent)) {
|
|
399
|
+
indent = _.repeat(INDENT, indent);
|
|
400
|
+
}
|
|
401
|
+
return (indentFirst ? indent : '') + str.split('\n').join(`\n${indent}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function pascalCase(str) {
|
|
405
|
+
return _.upperFirst(_.camelCase(str));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function readDirRec(dir, options) {
|
|
409
|
+
const dirExists = await fse.pathExists(dir);
|
|
410
|
+
if (!dirExists) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
const rootDir = _.get(options, 'rootDir', dir);
|
|
414
|
+
const files = await fse.readdir(dir);
|
|
415
|
+
const result = await mapPromise(files, async (file) => {
|
|
416
|
+
const filePath = path.join(dir, file);
|
|
417
|
+
const relFilePath = path.relative(rootDir, filePath);
|
|
418
|
+
const stats = await fse.stat(filePath);
|
|
419
|
+
if (_.has(options, 'filter') && !options.filter(relFilePath, stats)) {
|
|
420
|
+
return Promise.resolve();
|
|
421
|
+
}
|
|
422
|
+
if (stats.isDirectory()) {
|
|
423
|
+
return readDirRec(filePath, { ...options, rootDir });
|
|
424
|
+
} else if (stats.isFile()) {
|
|
425
|
+
return relFilePath;
|
|
426
|
+
} else {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
return _.chain(result).compact().flatten().value();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function readDirGlob(dir, options) {
|
|
434
|
+
let dirPattern = dir;
|
|
435
|
+
// default to listing all files recursively
|
|
436
|
+
if (!glob.hasMagic(dirPattern)) {
|
|
437
|
+
dirPattern = path.join(dir, '**/*');
|
|
438
|
+
}
|
|
439
|
+
return new Promise((resolve, reject) => {
|
|
440
|
+
glob(dirPattern, _.get(options, 'globOptions', {}), (err, files) => {
|
|
441
|
+
if (!_.isEmpty(err)) {
|
|
442
|
+
return reject(err);
|
|
443
|
+
}
|
|
444
|
+
resolve(
|
|
445
|
+
_.compact(
|
|
446
|
+
files.map((file) => {
|
|
447
|
+
if (_.has(options, 'filter') && !options.filter(file)) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
return file;
|
|
451
|
+
})
|
|
452
|
+
)
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function readDirRecSync(dir, options) {
|
|
459
|
+
let list = [];
|
|
460
|
+
const dirExists = fse.pathExistsSync(dir);
|
|
461
|
+
if (!dirExists) {
|
|
462
|
+
return list;
|
|
463
|
+
}
|
|
464
|
+
const files = fs.readdirSync(dir);
|
|
465
|
+
_.forEach(files, (file) => {
|
|
466
|
+
const filePath = path.join(dir, file);
|
|
467
|
+
if (_.has(options, 'filter') && !options.filter(filePath)) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const stats = fs.statSync(filePath);
|
|
471
|
+
if (stats.isDirectory()) {
|
|
472
|
+
list = list.concat(readDirRecSync(filePath, options));
|
|
473
|
+
} else if (stats.isFile()) {
|
|
474
|
+
list.push(filePath);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
return list;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function fieldPathToString(fieldPath) {
|
|
481
|
+
return _.reduce(
|
|
482
|
+
fieldPath,
|
|
483
|
+
(accumulator, fieldName, index) => {
|
|
484
|
+
if (_.isString(fieldName) && /\W/.test(fieldName)) {
|
|
485
|
+
// field name is a string with non alphanumeric character
|
|
486
|
+
accumulator += `['${fieldName}']`;
|
|
487
|
+
} else if (_.isNumber(fieldName)) {
|
|
488
|
+
accumulator += `[${fieldName}]`;
|
|
489
|
+
} else {
|
|
490
|
+
if (index > 0) {
|
|
491
|
+
accumulator += '.';
|
|
492
|
+
}
|
|
493
|
+
accumulator += fieldName;
|
|
494
|
+
}
|
|
495
|
+
return accumulator;
|
|
496
|
+
},
|
|
497
|
+
''
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function hrtimeAndPrint(time) {
|
|
502
|
+
const res = process.hrtime(time);
|
|
503
|
+
return printHRTime(res);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function printHRTime(time) {
|
|
507
|
+
const precision = 3;
|
|
508
|
+
if (time[0] > 0) {
|
|
509
|
+
return _.round(time[0] + time[1] / 1e9, precision) + 'sec';
|
|
510
|
+
} else if (time[1] >= 1e6) {
|
|
511
|
+
return _.round(time[1] / 1e6, precision) + 'ms';
|
|
512
|
+
} else if (time[1] >= 1e3) {
|
|
513
|
+
return _.round(time[1] / 1e3, precision) + 'µs';
|
|
514
|
+
} else if (time[1] >= 1e3) {
|
|
515
|
+
return _.round(time[1], precision) + 'ns';
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Recursively iterates over elements of a collection and invokes iteratee for each element.
|
|
521
|
+
*
|
|
522
|
+
* @param {*} value The value to iterate
|
|
523
|
+
* @param {Function} iteratee The iteratee function
|
|
524
|
+
* @param {string|number} key The key of the `value` if the `object` is an Object, or the index of the `value` if the `object` is an Array
|
|
525
|
+
* @param {Object} object The parent object of `value`.
|
|
526
|
+
*/
|
|
527
|
+
function forEachDeep(value, iteratee, key, object) {
|
|
528
|
+
iteratee(value, key, object);
|
|
529
|
+
if (_.isPlainObject(value)) {
|
|
530
|
+
_.forEach(value, (v, k) => {
|
|
531
|
+
forEachDeep(v, iteratee, k, value);
|
|
532
|
+
});
|
|
533
|
+
} else if (_.isArray(value)) {
|
|
534
|
+
_.forEach(value, (v, k) => {
|
|
535
|
+
forEachDeep(v, iteratee, k, value);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Gets the value at the first path of object having non undefined value.
|
|
542
|
+
* If all paths resolve to undefined values, the defaultValue is returned.
|
|
543
|
+
*
|
|
544
|
+
* @param {Object} object The object to query.
|
|
545
|
+
* @param {Array<String | Array<String>>} paths The property paths to search for.
|
|
546
|
+
* @param {*} [defaultValue] The value returned if all paths resolve to undefined values
|
|
547
|
+
* @returns {*}
|
|
548
|
+
*/
|
|
549
|
+
function getFirst(object, paths, defaultValue) {
|
|
550
|
+
let result = _(object).at(paths).reject(_.isUndefined).first();
|
|
551
|
+
return _.isUndefined(result) ? defaultValue : result;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
*
|
|
556
|
+
* @param {*} value
|
|
557
|
+
* @param {Function} iteratee Function (value: any, fieldPath: Array, stack: Array)
|
|
558
|
+
* @param {object} [options]
|
|
559
|
+
* @param {boolean} options.iterateCollections. Default: true
|
|
560
|
+
* @param {boolean} options.iterateScalars. Default: true
|
|
561
|
+
* @returns {*}
|
|
562
|
+
*/
|
|
563
|
+
function mapDeep(value, iteratee, options, _keyPath, _objectStack) {
|
|
564
|
+
let iterate;
|
|
565
|
+
if (_.isPlainObject(value) || _.isArray(value)) {
|
|
566
|
+
iterate = _.get(options, 'iterateCollections', true);
|
|
567
|
+
} else {
|
|
568
|
+
iterate = _.get(options, 'iterateScalars', true);
|
|
569
|
+
}
|
|
570
|
+
if (iterate) {
|
|
571
|
+
value = iteratee(value, _keyPath, _objectStack);
|
|
572
|
+
}
|
|
573
|
+
_keyPath = _keyPath || [];
|
|
574
|
+
_objectStack = _objectStack || [];
|
|
575
|
+
if (_.isPlainObject(value)) {
|
|
576
|
+
value = _.mapValues(value, (val, key) => {
|
|
577
|
+
return mapDeep(val, iteratee, options, _.concat(_keyPath, key), _.concat(_objectStack, value));
|
|
578
|
+
});
|
|
579
|
+
} else if (_.isArray(value)) {
|
|
580
|
+
value = _.map(value, (val, key) => {
|
|
581
|
+
return mapDeep(val, iteratee, options, _.concat(_keyPath, key), _.concat(_objectStack, [value]));
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
return value;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function getFirstExistingFile(fileNames, inputDir) {
|
|
588
|
+
return findPromise(fileNames, (fileName) => {
|
|
589
|
+
const absPath = path.resolve(inputDir, fileName);
|
|
590
|
+
return fse.exists(absPath);
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async function parseFirstExistingFile(fileNames, inputDir) {
|
|
595
|
+
const filePath = await getFirstExistingFile(fileNames, inputDir);
|
|
596
|
+
if (filePath) {
|
|
597
|
+
return await parseFile(filePath);
|
|
598
|
+
} else {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function getFirstExistingFileSync(fileNames, inputDir) {
|
|
604
|
+
return _.chain(fileNames)
|
|
605
|
+
.map((fileName) => path.resolve(inputDir, fileName))
|
|
606
|
+
.find((filePath) => fs.existsSync(filePath))
|
|
607
|
+
.value();
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function parseFirstExistingFileSync(fileNames, inputDir) {
|
|
611
|
+
let filePath = getFirstExistingFileSync(fileNames, inputDir);
|
|
612
|
+
if (filePath) {
|
|
613
|
+
return parseFileSync(filePath);
|
|
614
|
+
} else {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function parseFile(filePath) {
|
|
620
|
+
return fse.readFile(filePath, 'utf8').then((data) => {
|
|
621
|
+
return parseDataByFilePath(data, filePath);
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function parseFileSync(filePath) {
|
|
626
|
+
let data = fs.readFileSync(filePath, 'utf8');
|
|
627
|
+
return parseDataByFilePath(data, filePath);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function parseDataByFilePath(string, filePath) {
|
|
631
|
+
const extension = path.extname(filePath).substring(1);
|
|
632
|
+
let data;
|
|
633
|
+
switch (extension) {
|
|
634
|
+
case 'yml':
|
|
635
|
+
case 'yaml':
|
|
636
|
+
string = string.replace(/\n---\s*$/, '');
|
|
637
|
+
data = yaml.safeLoad(string, { schema: yaml.JSON_SCHEMA });
|
|
638
|
+
break;
|
|
639
|
+
case 'json':
|
|
640
|
+
data = JSON.parse(string);
|
|
641
|
+
break;
|
|
642
|
+
case 'toml':
|
|
643
|
+
data = toml.parse(string);
|
|
644
|
+
break;
|
|
645
|
+
case 'markdown':
|
|
646
|
+
case 'mdx':
|
|
647
|
+
case 'md':
|
|
648
|
+
data = parseMarkdownWithFrontMatter(string);
|
|
649
|
+
break;
|
|
650
|
+
case 'js':
|
|
651
|
+
case 'jsx':
|
|
652
|
+
data = string;
|
|
653
|
+
break;
|
|
654
|
+
default:
|
|
655
|
+
throw new Error(`parseDataByFilePath error, extension '${extension}' of file ${filePath} is not supported`);
|
|
656
|
+
}
|
|
657
|
+
return data;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function outputData(filePath, data) {
|
|
661
|
+
const res = stringifyDataByFilePath(data, filePath);
|
|
662
|
+
return fse.outputFile(filePath, res);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
async function outputDataIfNeeded(filePath, data) {
|
|
666
|
+
const res = stringifyDataByFilePath(data, filePath);
|
|
667
|
+
const fileExists = await fse.pathExists(filePath);
|
|
668
|
+
const existingContent = fileExists ? await fse.readFile(filePath, 'utf8') : null;
|
|
669
|
+
if (!fileExists || res !== existingContent) {
|
|
670
|
+
await fse.outputFile(filePath, res);
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function outputDataSync(filePath, data) {
|
|
677
|
+
const res = stringifyDataByFilePath(data, filePath);
|
|
678
|
+
fse.outputFileSync(filePath, res);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function stringifyDataByFilePath(data, filePath) {
|
|
682
|
+
const extension = path.extname(filePath).substring(1);
|
|
683
|
+
let result;
|
|
684
|
+
switch (extension) {
|
|
685
|
+
case 'yml':
|
|
686
|
+
case 'yaml':
|
|
687
|
+
result = yaml.safeDump(data, { noRefs: true });
|
|
688
|
+
break;
|
|
689
|
+
case 'json':
|
|
690
|
+
result = JSON.stringify(data, null, 4);
|
|
691
|
+
break;
|
|
692
|
+
case 'toml':
|
|
693
|
+
result = toml.stringify(data);
|
|
694
|
+
break;
|
|
695
|
+
case 'markdown':
|
|
696
|
+
case 'mdx':
|
|
697
|
+
case 'md':
|
|
698
|
+
result = '---\n' + yaml.safeDump(data.frontmatter, { noRefs: true }) + '---\n' + _.get(data, 'markdown', '');
|
|
699
|
+
break;
|
|
700
|
+
case 'js':
|
|
701
|
+
case 'jsx':
|
|
702
|
+
result = data;
|
|
703
|
+
break;
|
|
704
|
+
default:
|
|
705
|
+
throw new Error(`stringifyDataByFilePath error, extension '${extension}' of file ${filePath} is not supported`);
|
|
706
|
+
}
|
|
707
|
+
return result;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function parseMarkdownWithFrontMatter(string) {
|
|
711
|
+
string = string.replace('\r\n', '\n');
|
|
712
|
+
let frontmatter = null;
|
|
713
|
+
let markdown = string;
|
|
714
|
+
let frontMatterTypes = [
|
|
715
|
+
{
|
|
716
|
+
type: 'yaml',
|
|
717
|
+
startDelimiter: '---\n',
|
|
718
|
+
endDelimiter: '\n---',
|
|
719
|
+
parse: (string) => yaml.safeLoad(string, { schema: yaml.JSON_SCHEMA })
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
type: 'toml',
|
|
723
|
+
startDelimiter: '+++\n',
|
|
724
|
+
endDelimiter: '\n+++',
|
|
725
|
+
parse: (string) => toml.parse(string)
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
type: 'jsonmd',
|
|
729
|
+
startDelimiter: '---json\n',
|
|
730
|
+
endDelimiter: '\n---',
|
|
731
|
+
parse: (string) => JSON.parse(string)
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
type: 'json',
|
|
735
|
+
startDelimiter: '{\n',
|
|
736
|
+
endDelimiter: '\n}',
|
|
737
|
+
parse: (string) => JSON.parse(`{${string}}`)
|
|
738
|
+
}
|
|
739
|
+
];
|
|
740
|
+
_.forEach(frontMatterTypes, (fmType) => {
|
|
741
|
+
if (string.startsWith(fmType.startDelimiter)) {
|
|
742
|
+
let index = string.indexOf(fmType.endDelimiter);
|
|
743
|
+
if (index !== -1) {
|
|
744
|
+
// The end delimiter must be followed by EOF or by a new line (possibly preceded with spaces)
|
|
745
|
+
// For example ("." used for spaces):
|
|
746
|
+
// |---
|
|
747
|
+
// |title: Title
|
|
748
|
+
// |---...
|
|
749
|
+
// |
|
|
750
|
+
// |Markdown Content
|
|
751
|
+
// |
|
|
752
|
+
// "index" points to the beginning of the second "---"
|
|
753
|
+
// "endDelimEndIndex" points to the end of the second "---"
|
|
754
|
+
// "afterEndDelim" is everything after the second "---"
|
|
755
|
+
// "afterEndDelimMatch" is the matched "...\n" after the second "---"
|
|
756
|
+
// frontmatter will be: {title: "Title"}
|
|
757
|
+
// markdown will be "\nMarkdown Content\n" (the first \n after end delimiter is discarded)
|
|
758
|
+
let endDelimEndIndex = index + fmType.endDelimiter.length;
|
|
759
|
+
let afterEndDelim = string.substring(endDelimEndIndex);
|
|
760
|
+
let afterEndDelimMatch = afterEndDelim.match(/^\s*?(\n|$)/);
|
|
761
|
+
if (afterEndDelimMatch) {
|
|
762
|
+
let data = string.substring(fmType.startDelimiter.length, index);
|
|
763
|
+
frontmatter = fmType.parse(data);
|
|
764
|
+
markdown = afterEndDelim.substring(afterEndDelimMatch[0].length);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
return {
|
|
770
|
+
frontmatter: frontmatter,
|
|
771
|
+
markdown: markdown
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function deepFreeze(obj) {
|
|
776
|
+
Object.freeze(obj);
|
|
777
|
+
|
|
778
|
+
Object.getOwnPropertyNames(obj).forEach((prop) => {
|
|
779
|
+
if (
|
|
780
|
+
obj.hasOwnProperty(prop) &&
|
|
781
|
+
obj[prop] !== null &&
|
|
782
|
+
(typeof obj[prop] === 'object' || typeof obj[prop] === 'function') &&
|
|
783
|
+
!Object.isFrozen(obj[prop])
|
|
784
|
+
) {
|
|
785
|
+
this.deepFreeze(obj[prop]);
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
return obj;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function failFunctionWithTag(tag) {
|
|
793
|
+
return function fail(message) {
|
|
794
|
+
throw new Error(`[${tag}] ${message}`);
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function assertFunctionWithFail(fail) {
|
|
799
|
+
return function assert(value, message) {
|
|
800
|
+
if (!value) {
|
|
801
|
+
fail(message);
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function createLogger(scope, transport) {
|
|
807
|
+
const levels = ['log', 'info', 'debug', 'error'];
|
|
808
|
+
const logger = transport || console;
|
|
809
|
+
const noop = () => {};
|
|
810
|
+
const obj = {};
|
|
811
|
+
|
|
812
|
+
levels.forEach((level) => {
|
|
813
|
+
obj[level] = (...args) => {
|
|
814
|
+
(logger[level] || noop)(`[${scope}]`, ...args);
|
|
815
|
+
};
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
return obj;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function logObject(object, title) {
|
|
822
|
+
const label = title ? title : '';
|
|
823
|
+
console.group(label);
|
|
824
|
+
_.forEach(object, (value, key) => {
|
|
825
|
+
if (_.isString(value)) {
|
|
826
|
+
value = `"${value}"`;
|
|
827
|
+
}
|
|
828
|
+
console.log(`${key}: ${chalk.green(value)}`);
|
|
829
|
+
});
|
|
830
|
+
console.groupEnd(label);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function joinPathAndGlob(pathStr, glob) {
|
|
834
|
+
glob = globToArray(glob);
|
|
835
|
+
return _.map(glob, (globPart) => _.compact([pathStr, globPart]).join('/'));
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function globToArray(glob) {
|
|
839
|
+
return _.chain(glob)
|
|
840
|
+
.castArray()
|
|
841
|
+
.compact()
|
|
842
|
+
.reduce((accum, globPart) => {
|
|
843
|
+
const globParts = _.chain(globPart).trim('{}').split(',').compact().value();
|
|
844
|
+
return _.concat(accum, globParts);
|
|
845
|
+
}, [])
|
|
846
|
+
.value();
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Inverse of _.toPath()
|
|
851
|
+
*
|
|
852
|
+
* fromPath(['foo', 'hello.world', 'bar'])
|
|
853
|
+
* => 'foo["hello.world"].bar'
|
|
854
|
+
*
|
|
855
|
+
* @param {Array} pathArray
|
|
856
|
+
* @return {String}
|
|
857
|
+
*/
|
|
858
|
+
function fromPath(pathArray) {
|
|
859
|
+
return _.reduce(
|
|
860
|
+
pathArray,
|
|
861
|
+
(accum, pathPart) => {
|
|
862
|
+
if (_.isString(pathPart) && pathPart.indexOf('.') !== -1) {
|
|
863
|
+
return accum + `["${pathPart}"]`;
|
|
864
|
+
}
|
|
865
|
+
return accum + (accum ? '.' : '') + pathPart;
|
|
866
|
+
},
|
|
867
|
+
''
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function omitDeep(object, paths) {
|
|
872
|
+
if (_.isPlainObject(object)) {
|
|
873
|
+
return _.mapValues(_.omit(object, paths), (val) => omitDeep(val, paths));
|
|
874
|
+
} else if (_.isArray(object)) {
|
|
875
|
+
return _.map(object, (val) => omitDeep(val, paths));
|
|
876
|
+
}
|
|
877
|
+
return object;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Reduces the provided `data` using the provided reducer function `reducerFunc`
|
|
882
|
+
* into an object with `data` and `errors` attributes.
|
|
883
|
+
*
|
|
884
|
+
* For every item in the provided `data, the reducer function is invoked with
|
|
885
|
+
* `value` and `key` arguments. The reducer function should return an object
|
|
886
|
+
* with optional `data` and `error` properties, or `null`.
|
|
887
|
+
*
|
|
888
|
+
* ```
|
|
889
|
+
* reducerFunc(value, key) => { data, errors }
|
|
890
|
+
* ```
|
|
891
|
+
*
|
|
892
|
+
* When reducer function returns an object with a `data` property, its value
|
|
893
|
+
* is added to the `data` property of final result. If the original `data` is an
|
|
894
|
+
* object, then the `data` returned by the reducer function is added under the
|
|
895
|
+
* same `key` that was passed to the reducer function. If the original `data` is
|
|
896
|
+
* an array, then the value is pushed to the reduced data.
|
|
897
|
+
* If `data` property is missing, the reduced data will not include that item.
|
|
898
|
+
*
|
|
899
|
+
* When reducer function returns an object with `errors`, which can be an array
|
|
900
|
+
* of error messages or a single error message, these errors are added to the
|
|
901
|
+
* reduced result under `errors` property.
|
|
902
|
+
*
|
|
903
|
+
* @param {Array|Object} data
|
|
904
|
+
* @param {Function} reducerFunc
|
|
905
|
+
* @return {{data: Array|Object, errors: Array}}
|
|
906
|
+
*/
|
|
907
|
+
function dataReducerSync(data, reducerFunc) {
|
|
908
|
+
const isArray = Array.isArray(data);
|
|
909
|
+
const accum = {
|
|
910
|
+
data: isArray ? [] : {},
|
|
911
|
+
errors: []
|
|
912
|
+
};
|
|
913
|
+
const accumFunc = isArray ? (accum, value, key) => (accum.data = accum.data.concat(value)) : (accum, value, key) => (accum.data[key] = value);
|
|
914
|
+
|
|
915
|
+
const reducer = (accum, value, key) => {
|
|
916
|
+
const result = reducerFunc(value, key);
|
|
917
|
+
if (_.has(result, 'data')) {
|
|
918
|
+
const resValue = _.get(result, 'data');
|
|
919
|
+
accumFunc(accum, resValue, key);
|
|
920
|
+
}
|
|
921
|
+
if (_.has(result, 'errors')) {
|
|
922
|
+
accum.errors = accum.errors.concat(result.errors);
|
|
923
|
+
}
|
|
924
|
+
return accum;
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
return _.reduce(data, reducer, accum);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Same as dataReducerSync but receives asynchronous reducerFunc
|
|
932
|
+
*
|
|
933
|
+
* @param {Array|Object} data
|
|
934
|
+
* @param {Function} reducerFunc
|
|
935
|
+
* @return {{data: Array|Object, errors: Array}}
|
|
936
|
+
*/
|
|
937
|
+
async function dataReducer(data, reducerFunc) {
|
|
938
|
+
const isArray = Array.isArray(data);
|
|
939
|
+
const accum = {
|
|
940
|
+
data: isArray ? [] : {},
|
|
941
|
+
errors: []
|
|
942
|
+
};
|
|
943
|
+
const reduceFunc = isArray ? reducePromise : reduceObjectPromise;
|
|
944
|
+
const accumFunc = isArray ? (accum, value, key) => (accum.data = accum.data.concat(value)) : (accum, value, key) => (accum.data[key] = value);
|
|
945
|
+
|
|
946
|
+
const reducer = async (accum, value, key) => {
|
|
947
|
+
const result = await reducerFunc(value, key);
|
|
948
|
+
if (_.has(result, 'data')) {
|
|
949
|
+
const resValue = _.get(result, 'data');
|
|
950
|
+
accumFunc(accum, resValue, key);
|
|
951
|
+
}
|
|
952
|
+
if (_.has(result, 'errors')) {
|
|
953
|
+
accum.errors = accum.errors.concat(result.errors);
|
|
954
|
+
}
|
|
955
|
+
return accum;
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
return reduceFunc(data, reducer, accum);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
const JSX_ENCODE_ENTITIES = {
|
|
962
|
+
'{': '{',
|
|
963
|
+
'}': '}',
|
|
964
|
+
'<': '<',
|
|
965
|
+
'>': '>'
|
|
966
|
+
};
|
|
967
|
+
const JSX_DECODE_ENTITIES = _.invert(JSX_ENCODE_ENTITIES);
|
|
968
|
+
|
|
969
|
+
function encodeJsx(data) {
|
|
970
|
+
return (
|
|
971
|
+
data &&
|
|
972
|
+
Object.keys(JSX_ENCODE_ENTITIES).reduce((accum, entity) => {
|
|
973
|
+
return accum.replace(entity, JSX_ENCODE_ENTITIES[entity]);
|
|
974
|
+
}, data)
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function decodeJsx(data) {
|
|
979
|
+
return (
|
|
980
|
+
data &&
|
|
981
|
+
Object.keys(JSX_DECODE_ENTITIES).reduce((accum, entity) => {
|
|
982
|
+
return accum.replace(entity, JSX_DECODE_ENTITIES[entity]);
|
|
983
|
+
}, data)
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function replaceInRange(str, range, stringToInsert) {
|
|
988
|
+
return str.substr(0, range[0]) + stringToInsert + str.substr(range[1], str.length);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function isRelevantReactData(data) {
|
|
992
|
+
return (
|
|
993
|
+
data &&
|
|
994
|
+
data.length < 100 * 1024 && // 100kb
|
|
995
|
+
(data.includes('react') || data.includes('React'))
|
|
996
|
+
);
|
|
997
|
+
}
|