sculp-js 0.0.1
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/LICENSE.md +21 -0
- package/README.md +3 -0
- package/lib/cjs/array.js +217 -0
- package/lib/cjs/async.js +63 -0
- package/lib/cjs/clipboard.js +36 -0
- package/lib/cjs/cookie.js +63 -0
- package/lib/cjs/date.js +114 -0
- package/lib/cjs/dom.js +152 -0
- package/lib/cjs/download.js +82 -0
- package/lib/cjs/easing.js +40 -0
- package/lib/cjs/file.js +30 -0
- package/lib/cjs/index.js +41 -0
- package/lib/cjs/object.js +241 -0
- package/lib/cjs/path.js +66 -0
- package/lib/cjs/qs.js +71 -0
- package/lib/cjs/string.js +134 -0
- package/lib/cjs/type.js +47 -0
- package/lib/cjs/url.js +84 -0
- package/lib/cjs/watermark.js +91 -0
- package/lib/es/array.js +208 -0
- package/lib/es/async.js +60 -0
- package/lib/es/clipboard.js +34 -0
- package/lib/es/cookie.js +59 -0
- package/lib/es/date.js +110 -0
- package/lib/es/dom.js +143 -0
- package/lib/es/download.js +77 -0
- package/lib/es/easing.js +38 -0
- package/lib/es/file.js +28 -0
- package/lib/es/index.js +35 -0
- package/lib/es/object.js +228 -0
- package/lib/es/path.js +63 -0
- package/lib/es/qs.js +68 -0
- package/lib/es/string.js +124 -0
- package/lib/es/type.js +28 -0
- package/lib/es/url.js +79 -0
- package/lib/es/watermark.js +89 -0
- package/lib/index.d.ts +644 -0
- package/lib/umd/index.js +1458 -0
- package/package.json +80 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var url = require('./url.js');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 通过打开新窗口的方式下载
|
|
13
|
+
* @param {string} url
|
|
14
|
+
* @param {LooseParams} params
|
|
15
|
+
*/
|
|
16
|
+
const downloadURL = (url$1, params) => {
|
|
17
|
+
window.open(params ? url.urlSetParams(url$1, params) : url$1);
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 通过 A 链接的方式下载
|
|
21
|
+
* @param {string} href
|
|
22
|
+
* @param {string} filename
|
|
23
|
+
*/
|
|
24
|
+
const downloadHref = (href, filename) => {
|
|
25
|
+
const eleLink = document.createElement('a');
|
|
26
|
+
eleLink.download = filename;
|
|
27
|
+
eleLink.style.display = 'none';
|
|
28
|
+
eleLink.href = href;
|
|
29
|
+
document.body.appendChild(eleLink);
|
|
30
|
+
eleLink.click();
|
|
31
|
+
setTimeout(() => document.body.removeChild(eleLink));
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* 将大文件对象通过 A 链接的方式下载
|
|
35
|
+
* @param {Blob} blob
|
|
36
|
+
* @param {string} filename
|
|
37
|
+
*/
|
|
38
|
+
const downloadBlob = (blob, filename) => {
|
|
39
|
+
const objURL = URL.createObjectURL(blob);
|
|
40
|
+
downloadHref(objURL, filename);
|
|
41
|
+
setTimeout(() => URL.revokeObjectURL(objURL));
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* 将指定数据格式通过 A 链接的方式下载
|
|
45
|
+
* @param {AnyObject | AnyObject[]} data
|
|
46
|
+
* @param {FileType} fileType 支持 json/csv/xls/xlsx 四种格式
|
|
47
|
+
* @param {string} filename
|
|
48
|
+
* @param {string[]} [headers]
|
|
49
|
+
*/
|
|
50
|
+
const downloadData = (data, fileType, filename, headers) => {
|
|
51
|
+
filename = filename.replace(`.${fileType}`, '') + `.${fileType}`;
|
|
52
|
+
if (fileType === 'json') {
|
|
53
|
+
const blob = new Blob([JSON.stringify(data, null, 4)]);
|
|
54
|
+
downloadBlob(blob, filename);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// xlsx实际生成的也为csv,仅后缀名名不同
|
|
58
|
+
if (!headers || !headers.length)
|
|
59
|
+
throw new Error('未传入表头数据');
|
|
60
|
+
if (!Array.isArray(data))
|
|
61
|
+
throw new Error('data error! expected array!');
|
|
62
|
+
const headerStr = headers.join(',') + '\n';
|
|
63
|
+
let bodyStr = '';
|
|
64
|
+
data.forEach(row => {
|
|
65
|
+
// \t防止数字被科学计数法显示
|
|
66
|
+
bodyStr += Object.values(row).join(',\t') + ',\n';
|
|
67
|
+
});
|
|
68
|
+
const MIMETypes = {
|
|
69
|
+
csv: 'text/csv',
|
|
70
|
+
xls: 'application/vnd.ms-excel',
|
|
71
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
72
|
+
};
|
|
73
|
+
// encodeURIComponent解决中文乱码
|
|
74
|
+
const href = 'data:' + MIMETypes[fileType] + ';charset=utf-8,\ufeff' + encodeURIComponent(headerStr + bodyStr);
|
|
75
|
+
downloadHref(href, filename);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
exports.downloadBlob = downloadBlob;
|
|
80
|
+
exports.downloadData = downloadData;
|
|
81
|
+
exports.downloadHref = downloadHref;
|
|
82
|
+
exports.downloadURL = downloadURL;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var bezier = require('bezier-easing');
|
|
10
|
+
var type = require('./type.js');
|
|
11
|
+
|
|
12
|
+
// @ref https://cubic-bezier.com/
|
|
13
|
+
const easingDefines = {
|
|
14
|
+
linear: [0, 0, 1, 1],
|
|
15
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
16
|
+
'ease-in': [0.42, 0, 1, 1],
|
|
17
|
+
'ease-out': [0, 0, 0.58, 1],
|
|
18
|
+
'ease-in-out': [0.42, 0, 0.58, 1]
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 缓冲函数化,用于 js 计算缓冲进度
|
|
22
|
+
* @param {EasingNameOrDefine} [name=linear]
|
|
23
|
+
* @returns {EasingFunction}
|
|
24
|
+
*/
|
|
25
|
+
function easingFunctional(name) {
|
|
26
|
+
let fn;
|
|
27
|
+
if (type.isArray(name)) {
|
|
28
|
+
fn = bezier(...name);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const define = easingDefines[name];
|
|
32
|
+
if (!define) {
|
|
33
|
+
throw new Error(`${name} 缓冲函数未定义`);
|
|
34
|
+
}
|
|
35
|
+
fn = bezier(...define);
|
|
36
|
+
}
|
|
37
|
+
return (input) => fn(Math.max(0, Math.min(input, 1)));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.easingFunctional = easingFunctional;
|
package/lib/cjs/file.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 选择本地文件
|
|
11
|
+
* @param {function} changeCb 选择文件回调
|
|
12
|
+
* @return {*}
|
|
13
|
+
*/
|
|
14
|
+
function chooseLocalFile({ accept }, changeCb) {
|
|
15
|
+
const inputObj = document.createElement('input');
|
|
16
|
+
inputObj.setAttribute('id', String(Date.now()));
|
|
17
|
+
inputObj.setAttribute('type', 'file');
|
|
18
|
+
inputObj.setAttribute('style', 'visibility:hidden');
|
|
19
|
+
inputObj.setAttribute('accept', accept);
|
|
20
|
+
document.body.appendChild(inputObj);
|
|
21
|
+
inputObj.click();
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
inputObj.onchange = (e) => {
|
|
24
|
+
changeCb(e.target.files);
|
|
25
|
+
setTimeout(() => document.body.removeChild(inputObj));
|
|
26
|
+
};
|
|
27
|
+
return inputObj;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
exports.chooseLocalFile = chooseLocalFile;
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var array = require('./array.js');
|
|
10
|
+
var clipboard = require('./clipboard.js');
|
|
11
|
+
var cookie = require('./cookie.js');
|
|
12
|
+
var date = require('./date.js');
|
|
13
|
+
var dom = require('./dom.js');
|
|
14
|
+
var download = require('./download.js');
|
|
15
|
+
var object = require('./object.js');
|
|
16
|
+
var path = require('./path.js');
|
|
17
|
+
var qs = require('./qs.js');
|
|
18
|
+
var string = require('./string.js');
|
|
19
|
+
var type = require('./type.js');
|
|
20
|
+
var url = require('./url.js');
|
|
21
|
+
var async = require('./async.js');
|
|
22
|
+
var file = require('./file.js');
|
|
23
|
+
var watermark = require('./watermark.js');
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
exports.array = array;
|
|
28
|
+
exports.clipboard = clipboard;
|
|
29
|
+
exports.cookie = cookie;
|
|
30
|
+
exports.date = date;
|
|
31
|
+
exports.dom = dom;
|
|
32
|
+
exports.download = download;
|
|
33
|
+
exports.object = object;
|
|
34
|
+
exports.path = path;
|
|
35
|
+
exports.qs = qs;
|
|
36
|
+
exports.string = string;
|
|
37
|
+
exports.type = type;
|
|
38
|
+
exports.url = url;
|
|
39
|
+
exports.async = async;
|
|
40
|
+
exports.file = file;
|
|
41
|
+
exports.genCanvasWM = watermark.genCanvasWM;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var type = require('./type.js');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 判断对象是否为纯对象
|
|
13
|
+
* @param {object} obj
|
|
14
|
+
* @returns {boolean}
|
|
15
|
+
*/
|
|
16
|
+
const isPlainObject = (obj) => {
|
|
17
|
+
if (!type.isObject(obj))
|
|
18
|
+
return false;
|
|
19
|
+
const proto = Object.getPrototypeOf(obj);
|
|
20
|
+
// 对象无原型
|
|
21
|
+
if (!proto)
|
|
22
|
+
return true;
|
|
23
|
+
// 是否对象直接实例
|
|
24
|
+
return proto === Object.prototype;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* 判断对象内是否有该静态属性
|
|
28
|
+
* @param {object} obj
|
|
29
|
+
* @param {string} key
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
const objectHas = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
33
|
+
/**
|
|
34
|
+
* 遍历对象,返回 false 中断遍历
|
|
35
|
+
* @param {O} obj
|
|
36
|
+
* @param {(val: O[keyof O], key: keyof O) => (boolean | void)} iterator
|
|
37
|
+
*/
|
|
38
|
+
const objectEach = (obj, iterator) => {
|
|
39
|
+
for (const key in obj) {
|
|
40
|
+
if (!objectHas(obj, key))
|
|
41
|
+
continue;
|
|
42
|
+
if (iterator(obj[key], key) === false)
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* 异步遍历对象,返回 false 中断遍历
|
|
48
|
+
* @param {O} obj
|
|
49
|
+
* @param {(val: O[keyof O], key: keyof O) => (boolean | void)} iterator
|
|
50
|
+
*/
|
|
51
|
+
async function objectEachAsync(obj, iterator) {
|
|
52
|
+
for (const key in obj) {
|
|
53
|
+
if (!objectHas(obj, key))
|
|
54
|
+
continue;
|
|
55
|
+
if ((await iterator(obj[key], key)) === false)
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 对象映射
|
|
61
|
+
* @param {O} obj
|
|
62
|
+
* @param {(val: O[keyof O], key: Extract<keyof O, string>) => any} iterator
|
|
63
|
+
* @returns {Record<Extract<keyof O, string>, T>}
|
|
64
|
+
*/
|
|
65
|
+
function objectMap(obj, iterator) {
|
|
66
|
+
const obj2 = {};
|
|
67
|
+
for (const key in obj) {
|
|
68
|
+
if (!objectHas(obj, key))
|
|
69
|
+
continue;
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
71
|
+
obj2[key] = iterator(obj[key], key);
|
|
72
|
+
}
|
|
73
|
+
return obj2;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 对象提取
|
|
77
|
+
* @param {O} obj
|
|
78
|
+
* @param {K} keys
|
|
79
|
+
* @returns {Pick<O, ArrayElements<K>>}
|
|
80
|
+
*/
|
|
81
|
+
function objectPick(obj, keys) {
|
|
82
|
+
const obj2 = {};
|
|
83
|
+
objectEach(obj, (v, k) => {
|
|
84
|
+
if (keys.includes(k)) {
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
obj2[k] = v;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return obj2;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 对象祛除
|
|
94
|
+
* @param {O} obj
|
|
95
|
+
* @param {K} keys
|
|
96
|
+
* @returns {Pick<O, ArrayElements<K>>}
|
|
97
|
+
*/
|
|
98
|
+
function objectOmit(obj, keys) {
|
|
99
|
+
const obj2 = {};
|
|
100
|
+
objectEach(obj, (v, k) => {
|
|
101
|
+
if (!keys.includes(k)) {
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
obj2[k] = v;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return obj2;
|
|
108
|
+
}
|
|
109
|
+
const merge = (map, source, target) => {
|
|
110
|
+
if (type.isUndefined(target))
|
|
111
|
+
return source;
|
|
112
|
+
const sourceType = type.default(source);
|
|
113
|
+
const targetType = type.default(target);
|
|
114
|
+
if (sourceType !== targetType) {
|
|
115
|
+
if (type.isArray(target))
|
|
116
|
+
return merge(map, [], target);
|
|
117
|
+
if (type.isObject(target))
|
|
118
|
+
return merge(map, {}, target);
|
|
119
|
+
return target;
|
|
120
|
+
}
|
|
121
|
+
// 朴素对象
|
|
122
|
+
if (isPlainObject(target)) {
|
|
123
|
+
const exist = map.get(target);
|
|
124
|
+
if (exist)
|
|
125
|
+
return exist;
|
|
126
|
+
map.set(target, source);
|
|
127
|
+
objectEach(target, (val, key) => {
|
|
128
|
+
source[key] = merge(map, source[key], val);
|
|
129
|
+
});
|
|
130
|
+
return source;
|
|
131
|
+
}
|
|
132
|
+
// 数组
|
|
133
|
+
else if (type.isArray(target)) {
|
|
134
|
+
const exist = map.get(target);
|
|
135
|
+
if (exist)
|
|
136
|
+
return exist;
|
|
137
|
+
map.set(target, source);
|
|
138
|
+
target.forEach((val, index) => {
|
|
139
|
+
source[index] = merge(map, source[index], val);
|
|
140
|
+
});
|
|
141
|
+
return source;
|
|
142
|
+
}
|
|
143
|
+
return target;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* 对象合并,返回原始对象
|
|
147
|
+
* @param {ObjectAssignItem} source
|
|
148
|
+
* @param {ObjectAssignItem | undefined} targets
|
|
149
|
+
* @returns {R}
|
|
150
|
+
*/
|
|
151
|
+
const objectAssign = (source, ...targets) => {
|
|
152
|
+
const map = new Map();
|
|
153
|
+
for (let i = 0; i < targets.length; i++) {
|
|
154
|
+
const target = targets[i];
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
156
|
+
// @ts-ignore
|
|
157
|
+
source = merge(map, source, target);
|
|
158
|
+
}
|
|
159
|
+
map.clear();
|
|
160
|
+
return source;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* 对象填充
|
|
164
|
+
* @param {Partial<R>} source
|
|
165
|
+
* @param {Partial<R>} target
|
|
166
|
+
* @param {(s: Partial<R>, t: Partial<R>, key: keyof R) => boolean} fillable
|
|
167
|
+
* @returns {R}
|
|
168
|
+
*/
|
|
169
|
+
const objectFill = (source, target, fillable) => {
|
|
170
|
+
const _fillable = fillable || ((source, target, key) => source[key] === undefined);
|
|
171
|
+
objectEach(target, (val, key) => {
|
|
172
|
+
if (_fillable(source, target, key)) {
|
|
173
|
+
source[key] = val;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
return source;
|
|
177
|
+
};
|
|
178
|
+
function objectGet(obj, path, strict = false) {
|
|
179
|
+
path = path.replace(/\[(\w+)\]/g, '.$1');
|
|
180
|
+
path = path.replace(/^\./, '');
|
|
181
|
+
const keyArr = path.split('.');
|
|
182
|
+
let tempObj = obj;
|
|
183
|
+
let i = 0;
|
|
184
|
+
for (let len = keyArr.length; i < len - 1; ++i) {
|
|
185
|
+
const key = keyArr[i];
|
|
186
|
+
if (key in tempObj) {
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
|
|
188
|
+
tempObj = tempObj[key];
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
tempObj = undefined;
|
|
192
|
+
if (strict) {
|
|
193
|
+
throw new Error('[berry/js-utils/object] objectGet path 路径不正确');
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
200
|
+
p: tempObj,
|
|
201
|
+
k: tempObj ? keyArr[i] : undefined,
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
|
|
203
|
+
v: tempObj ? tempObj[keyArr[i]] : undefined
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 深拷贝堪称完全体 即:任何类型的数据都会被深拷贝
|
|
208
|
+
* @param {AnyObject | AnyArray} obj
|
|
209
|
+
* @param {WeakMap} map
|
|
210
|
+
* @return {AnyObject | AnyArray}
|
|
211
|
+
*/
|
|
212
|
+
function cloneDeep(obj, map = new WeakMap()) {
|
|
213
|
+
if (obj instanceof Date)
|
|
214
|
+
return new Date(obj);
|
|
215
|
+
if (obj instanceof RegExp)
|
|
216
|
+
return new RegExp(obj);
|
|
217
|
+
if (map.has(obj)) {
|
|
218
|
+
return map.get(obj);
|
|
219
|
+
}
|
|
220
|
+
const allDesc = Object.getOwnPropertyDescriptors(obj);
|
|
221
|
+
const cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
|
|
222
|
+
map.set(obj, cloneObj);
|
|
223
|
+
for (const key of Reflect.ownKeys(obj)) {
|
|
224
|
+
const value = obj[key];
|
|
225
|
+
cloneObj[key] = value instanceof Object && typeof value !== 'function' ? cloneDeep(value, map) : value;
|
|
226
|
+
}
|
|
227
|
+
return cloneObj;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
exports.cloneDeep = cloneDeep;
|
|
231
|
+
exports.isPlainObject = isPlainObject;
|
|
232
|
+
exports.objectAssign = objectAssign;
|
|
233
|
+
exports.objectEach = objectEach;
|
|
234
|
+
exports.objectEachAsync = objectEachAsync;
|
|
235
|
+
exports.objectFill = objectFill;
|
|
236
|
+
exports.objectGet = objectGet;
|
|
237
|
+
exports.objectHas = objectHas;
|
|
238
|
+
exports.objectMap = objectMap;
|
|
239
|
+
exports.objectMerge = objectAssign;
|
|
240
|
+
exports.objectOmit = objectOmit;
|
|
241
|
+
exports.objectPick = objectPick;
|
package/lib/cjs/path.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 标准化路径
|
|
11
|
+
* @param {string} path
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
const pathNormalize = (path) => {
|
|
15
|
+
const slicees = path
|
|
16
|
+
.replace(/\\/g, '/')
|
|
17
|
+
.replace(/\/{2,}/g, '/')
|
|
18
|
+
.replace(/\.{3,}/g, '..')
|
|
19
|
+
.replace(/\/\.\//g, '/')
|
|
20
|
+
.split('/')
|
|
21
|
+
.map(point => point.trim());
|
|
22
|
+
const isCurrentSlice = (slice) => slice === '.';
|
|
23
|
+
const isParentSlice = (slice) => slice === '..';
|
|
24
|
+
const points = [];
|
|
25
|
+
let inPoints = false;
|
|
26
|
+
const push = (point) => {
|
|
27
|
+
points.push(point);
|
|
28
|
+
};
|
|
29
|
+
const back = () => {
|
|
30
|
+
if (points.length === 0)
|
|
31
|
+
return;
|
|
32
|
+
const lastSlice = points[points.length - 1];
|
|
33
|
+
if (isParentSlice(lastSlice)) {
|
|
34
|
+
points.push('..');
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
points.pop();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
slicees.forEach(slice => {
|
|
41
|
+
const isCurrent = isCurrentSlice(slice);
|
|
42
|
+
const isParent = isParentSlice(slice);
|
|
43
|
+
// 未进入实际路径
|
|
44
|
+
if (!inPoints) {
|
|
45
|
+
push(slice);
|
|
46
|
+
inPoints = !isCurrent && !isParent;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (isCurrent)
|
|
50
|
+
return;
|
|
51
|
+
if (isParent)
|
|
52
|
+
return back();
|
|
53
|
+
push(slice);
|
|
54
|
+
});
|
|
55
|
+
return points.join('/');
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* 路径合并
|
|
59
|
+
* @param {string} from
|
|
60
|
+
* @param {string} to
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
const pathJoin = (from, ...to) => pathNormalize([from, ...to].join('/'));
|
|
64
|
+
|
|
65
|
+
exports.pathJoin = pathJoin;
|
|
66
|
+
exports.pathNormalize = pathNormalize;
|
package/lib/cjs/qs.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var object = require('./object.js');
|
|
10
|
+
var type = require('./type.js');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 解析查询参数,内部使用的是浏览器内置的 URLSearchParams 进行处理
|
|
14
|
+
* @param {string} queryString
|
|
15
|
+
* @returns {Params}
|
|
16
|
+
*/
|
|
17
|
+
const qsParse = (queryString) => {
|
|
18
|
+
const params = new URLSearchParams(queryString);
|
|
19
|
+
const result = {};
|
|
20
|
+
for (const [key, val] of params.entries()) {
|
|
21
|
+
if (type.isUndefined(result[key])) {
|
|
22
|
+
result[key] = val;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (type.isArray(result[key])) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
result[key] = params.getAll(key);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
const defaultReplacer = (val) => {
|
|
33
|
+
if (type.isString(val))
|
|
34
|
+
return val;
|
|
35
|
+
if (type.isNumber(val))
|
|
36
|
+
return String(val);
|
|
37
|
+
if (type.isBoolean(val))
|
|
38
|
+
return val ? 'true' : 'false';
|
|
39
|
+
if (type.isDate(val))
|
|
40
|
+
return val.toISOString();
|
|
41
|
+
return null;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* 字符化查询对象,内部使用的是浏览器内置的 URLSearchParams 进行处理
|
|
45
|
+
* @param {LooseParams} query
|
|
46
|
+
* @param {Replacer} replacer
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/
|
|
49
|
+
const qsStringify = (query, replacer = defaultReplacer) => {
|
|
50
|
+
const params = new URLSearchParams();
|
|
51
|
+
object.objectEach(query, (val, key) => {
|
|
52
|
+
if (type.isArray(val)) {
|
|
53
|
+
val.forEach(i => {
|
|
54
|
+
const replaced = replacer(i);
|
|
55
|
+
if (replaced === null)
|
|
56
|
+
return;
|
|
57
|
+
params.append(key.toString(), replaced);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const replaced = replacer(val);
|
|
62
|
+
if (replaced === null)
|
|
63
|
+
return;
|
|
64
|
+
params.set(key.toString(), replaced);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return params.toString();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
exports.qsParse = qsParse;
|
|
71
|
+
exports.qsStringify = qsStringify;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 将字符串转换为驼峰格式
|
|
11
|
+
* @param {string} string
|
|
12
|
+
* @param {boolean} [bigger] 是否大写第一个字母
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
const stringCamelCase = (string, bigger) => {
|
|
16
|
+
let string2 = string;
|
|
17
|
+
if (bigger) {
|
|
18
|
+
string2 = string.replace(/^./, origin => origin.toUpperCase());
|
|
19
|
+
}
|
|
20
|
+
const HUMP_RE = /[\s_-](.)/g;
|
|
21
|
+
return string2.replace(HUMP_RE, (orign, char) => char.toUpperCase());
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 将字符串转换为连字格式
|
|
25
|
+
* @param {string} string
|
|
26
|
+
* @param {string} [separator] 分隔符,默认是"-"(短横线)
|
|
27
|
+
* @returns {string}
|
|
28
|
+
*/
|
|
29
|
+
const stringKebabCase = (string, separator = '-') => {
|
|
30
|
+
const string2 = string.replace(/^./, origin => origin.toLowerCase());
|
|
31
|
+
return string2.replace(/[A-Z]/g, origin => `${separator}${origin.toLowerCase()}`);
|
|
32
|
+
};
|
|
33
|
+
const STRING_ARABIC_NUMERALS = '0123456789';
|
|
34
|
+
const STRING_LOWERCASE_ALPHA = 'abcdefghijklmnopqrstuvwxyz';
|
|
35
|
+
const STRING_UPPERCASE_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
36
|
+
const placeholderRE = /%[%sdo]/g;
|
|
37
|
+
/**
|
|
38
|
+
* 字符串格式化
|
|
39
|
+
* @example
|
|
40
|
+
* ```js
|
|
41
|
+
* stringFormat("My name is %s.", "zhangsan")
|
|
42
|
+
* // => "My name is zhangsan."
|
|
43
|
+
* ```
|
|
44
|
+
* @param {string} string 字符串模板,使用 %s 表示字符串,%d 表示数值,%o 表示对象,%% 表示百分号,参考 console.log
|
|
45
|
+
* @param args
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
const stringFormat = (string, ...args) => {
|
|
49
|
+
let index = 0;
|
|
50
|
+
const result = string.replace(placeholderRE, (origin) => {
|
|
51
|
+
const arg = args[index++];
|
|
52
|
+
switch (origin) {
|
|
53
|
+
case '%%':
|
|
54
|
+
index--;
|
|
55
|
+
return '%';
|
|
56
|
+
default:
|
|
57
|
+
case '%s':
|
|
58
|
+
return String(arg);
|
|
59
|
+
case '%d':
|
|
60
|
+
return String(Number(arg));
|
|
61
|
+
case '%o':
|
|
62
|
+
return JSON.stringify(arg);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return [result, ...args.splice(index).map(String)].join(' ');
|
|
66
|
+
};
|
|
67
|
+
const ev = (expression, data) => {
|
|
68
|
+
try {
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval,@typescript-eslint/no-unsafe-return
|
|
70
|
+
return new Function('with(arguments[0]){' +
|
|
71
|
+
/****/ `if(arguments[0].${expression} === undefined)throw "";` +
|
|
72
|
+
/****/
|
|
73
|
+
/****/ `return String(arguments[0].${expression})` +
|
|
74
|
+
'}')(data);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
throw new SyntaxError(`无法执行表达式:${expression}`);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const templateRE = /\${(.*?)}/g;
|
|
81
|
+
/**
|
|
82
|
+
* 字符串赋值
|
|
83
|
+
* @example
|
|
84
|
+
* ```js
|
|
85
|
+
* stringAssign('My name is ${user}.', { user: 'zhangsan' });
|
|
86
|
+
* // => "My name is zhangsan."
|
|
87
|
+
* ```
|
|
88
|
+
* @param {string} template
|
|
89
|
+
* @param {AnyObject} data
|
|
90
|
+
* @returns {string}
|
|
91
|
+
*/
|
|
92
|
+
const stringAssign = (template, data) => {
|
|
93
|
+
return template.replace(templateRE, (origin, expression) => ev(expression, data));
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* 字符串编码 HTML
|
|
97
|
+
* @example
|
|
98
|
+
* ```js
|
|
99
|
+
* stringEscapeHtml('<b>You & Me speak "xixi"</b>')
|
|
100
|
+
* // => "<b>You & Me speak "xixi"</b>"
|
|
101
|
+
* ```
|
|
102
|
+
* @param {string} html
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
const stringEscapeHtml = (html) => {
|
|
106
|
+
const htmlCharRE = /[&<>"]/g;
|
|
107
|
+
const htmlCharReplacements = {
|
|
108
|
+
'&': '&',
|
|
109
|
+
'<': '<',
|
|
110
|
+
'>': '>',
|
|
111
|
+
'"': '"'
|
|
112
|
+
};
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
116
|
+
return html.replace(htmlCharRE, $0 => htmlCharReplacements[$0]);
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* 字符串填充
|
|
120
|
+
* @param {number} length
|
|
121
|
+
* @param {string} value
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
const stringFill = (length, value = ' ') => new Array(length).fill(value).join('');
|
|
125
|
+
|
|
126
|
+
exports.STRING_ARABIC_NUMERALS = STRING_ARABIC_NUMERALS;
|
|
127
|
+
exports.STRING_LOWERCASE_ALPHA = STRING_LOWERCASE_ALPHA;
|
|
128
|
+
exports.STRING_UPPERCASE_ALPHA = STRING_UPPERCASE_ALPHA;
|
|
129
|
+
exports.stringAssign = stringAssign;
|
|
130
|
+
exports.stringCamelCase = stringCamelCase;
|
|
131
|
+
exports.stringEscapeHtml = stringEscapeHtml;
|
|
132
|
+
exports.stringFill = stringFill;
|
|
133
|
+
exports.stringFormat = stringFormat;
|
|
134
|
+
exports.stringKebabCase = stringKebabCase;
|