@anjianshi/utils 1.0.9 → 1.2.0
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/.eslintrc.cjs +1 -1
- package/env-browser/global.d.ts +1 -0
- package/env-browser/global.js +1 -0
- package/env-node/logging.d.ts +7 -6
- package/env-node/logging.js +5 -6
- package/init-dayjs.js +3 -4
- package/lang/string.d.ts +4 -9
- package/lang/string.js +4 -14
- package/lang/types.d.ts +2 -1
- package/lang/types.js +2 -2
- package/logging/adapt.d.ts +1 -1
- package/logging/adapt.js +3 -7
- package/package.json +6 -7
- package/src/.eslintrc.cjs +1 -5
- package/src/env-browser/global.ts +1 -0
- package/src/env-node/.eslintrc.cjs +1 -1
- package/src/env-node/logging.ts +5 -6
- package/src/init-dayjs.ts +3 -4
- package/src/lang/string.ts +4 -17
- package/src/lang/types.ts +8 -13
- package/src/logging/adapt.ts +3 -6
- package/src/logging/index.ts +1 -1
- package/src/url.ts +38 -33
- package/url.d.ts +26 -3
- package/url.js +29 -24
package/.eslintrc.cjs
CHANGED
package/env-browser/global.d.ts
CHANGED
package/env-browser/global.js
CHANGED
package/env-node/logging.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
1
2
|
import { type Logger, type LogInfo, LogHandler } from '../logging/index.js';
|
|
2
3
|
export * from '../logging/index.js';
|
|
3
4
|
/**
|
|
@@ -24,14 +25,14 @@ export declare class ConsoleHandler extends LogHandler {
|
|
|
24
25
|
};
|
|
25
26
|
};
|
|
26
27
|
static readonly levelColors: {
|
|
27
|
-
1:
|
|
28
|
-
2:
|
|
29
|
-
3:
|
|
30
|
-
4:
|
|
28
|
+
1: chalk.Chalk;
|
|
29
|
+
2: chalk.Chalk;
|
|
30
|
+
3: chalk.Chalk;
|
|
31
|
+
4: chalk.Chalk;
|
|
31
32
|
};
|
|
32
33
|
private static readonly loggerColors;
|
|
33
34
|
private static readonly loggerColorMap;
|
|
34
|
-
static getLoggerColor(logger: string): "green" | "yellow" | "blue" | "
|
|
35
|
+
static getLoggerColor(logger: string): "green" | "yellow" | "blue" | "magenta" | "cyan" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright";
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* 写入文件日志
|
|
@@ -47,7 +48,7 @@ export declare class FileHandler extends LogHandler {
|
|
|
47
48
|
readonly options: FileHandlerOptions;
|
|
48
49
|
constructor(options?: Partial<FileHandlerOptions>);
|
|
49
50
|
log(info: LogInfo): void;
|
|
50
|
-
protected
|
|
51
|
+
protected stringifyDataItem(item: unknown): string;
|
|
51
52
|
private buffer;
|
|
52
53
|
private bufferSize;
|
|
53
54
|
protected pushBuffer(...strings: string[]): void;
|
package/env-node/logging.js
CHANGED
|
@@ -7,7 +7,6 @@ import path from 'node:path';
|
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import dayjs from 'dayjs';
|
|
10
|
-
import { removeANSIColor } from '../lang/index.js';
|
|
11
10
|
import { logger as defaultLogger, LogLevel, LogHandler, formatters, } from '../logging/index.js';
|
|
12
11
|
export * from '../logging/index.js';
|
|
13
12
|
/**
|
|
@@ -21,9 +20,9 @@ export class ConsoleHandler extends LogHandler {
|
|
|
21
20
|
const levelName = formatters.level(info);
|
|
22
21
|
const loggerColor = chalk[ConsoleHandler.getLoggerColor(logger)];
|
|
23
22
|
const prefix = [
|
|
24
|
-
chalk.white(`[${formatters.
|
|
23
|
+
chalk.white(`[${formatters.time(info)}]`),
|
|
25
24
|
levelColor(`[${levelName}]`),
|
|
26
|
-
|
|
25
|
+
loggerColor(`[${logger}]`),
|
|
27
26
|
].join('');
|
|
28
27
|
method(prefix, ...args);
|
|
29
28
|
}
|
|
@@ -88,7 +87,7 @@ export class FileHandler extends LogHandler {
|
|
|
88
87
|
const itemStrings = [];
|
|
89
88
|
let totalLength = prefix.length;
|
|
90
89
|
for (const item of args) {
|
|
91
|
-
const itemString = this.
|
|
90
|
+
const itemString = this.stringifyDataItem(item);
|
|
92
91
|
// 截断过长的日志内容 Truncate overly long log messages
|
|
93
92
|
if (totalLength + itemString.length < this.options.maxLength) {
|
|
94
93
|
itemStrings.push((totalLength === prefix.length ? '' : ' ') + itemString);
|
|
@@ -101,9 +100,9 @@ export class FileHandler extends LogHandler {
|
|
|
101
100
|
}
|
|
102
101
|
this.pushBuffer(prefix, ...itemStrings, '\n');
|
|
103
102
|
}
|
|
104
|
-
|
|
103
|
+
stringifyDataItem(item) {
|
|
105
104
|
if (typeof item === 'string')
|
|
106
|
-
return
|
|
105
|
+
return item;
|
|
107
106
|
if (item instanceof Error)
|
|
108
107
|
return item.stack ?? String(item);
|
|
109
108
|
try {
|
package/init-dayjs.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { extend, locale } from 'dayjs';
|
|
2
2
|
import objectSupport from 'dayjs/plugin/objectSupport.js';
|
|
3
3
|
import 'dayjs/locale/zh-cn.js';
|
|
4
4
|
export function initDayJs() {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
dayjs.locale('zh-cn');
|
|
5
|
+
extend(objectSupport);
|
|
6
|
+
locale('zh-cn');
|
|
8
7
|
}
|
package/lang/string.d.ts
CHANGED
|
@@ -16,15 +16,10 @@ export declare function safeParseInt(value: string | number, fallback?: number,
|
|
|
16
16
|
*/
|
|
17
17
|
export declare function safeParseFloat(value: string | number, fallback: number): number;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* 返回人类可读的文件尺寸
|
|
20
|
+
* 来自:https://stackoverflow.com/a/14919494
|
|
21
21
|
*
|
|
22
|
-
* si: true
|
|
23
|
-
* dp:
|
|
22
|
+
* si: true 则使用 1000 进制,否则 1024 进制(默认 false)
|
|
23
|
+
* dp: 保留几位小数
|
|
24
24
|
*/
|
|
25
25
|
export declare function readableSize(bytes: number, si?: boolean, dp?: number): string;
|
|
26
|
-
/**
|
|
27
|
-
* 移除字符串中的 ASNI Color 修饰符
|
|
28
|
-
* https://stackoverflow.com/a/7150870/2815178
|
|
29
|
-
*/
|
|
30
|
-
export declare function removeANSIColor(string: string): string;
|
package/lang/string.js
CHANGED
|
@@ -30,7 +30,6 @@ export function keywordCompare(keyword, target) {
|
|
|
30
30
|
*
|
|
31
31
|
* 以 0 开头是特例,例如 019 和 12 两个字符串,按直觉还是应该 019 在前面,毕竟本质还是字符串排序。
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
33
|
* "123" "456" 数字排序
|
|
35
34
|
* "123你好" "133我好" 字符串排序
|
|
36
35
|
* "019" "12" 字符串排序
|
|
@@ -58,11 +57,11 @@ export function safeParseFloat(value, fallback) {
|
|
|
58
57
|
return isFinite(raw) ? raw : fallback;
|
|
59
58
|
}
|
|
60
59
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* 返回人类可读的文件尺寸
|
|
61
|
+
* 来自:https://stackoverflow.com/a/14919494
|
|
63
62
|
*
|
|
64
|
-
* si: true
|
|
65
|
-
* dp:
|
|
63
|
+
* si: true 则使用 1000 进制,否则 1024 进制(默认 false)
|
|
64
|
+
* dp: 保留几位小数
|
|
66
65
|
*/
|
|
67
66
|
export function readableSize(bytes, si = false, dp = 1) {
|
|
68
67
|
const thresh = si ? 1000 : 1024;
|
|
@@ -79,12 +78,3 @@ export function readableSize(bytes, si = false, dp = 1) {
|
|
|
79
78
|
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
|
80
79
|
return `${bytes.toFixed(dp)} ${units[u]}`;
|
|
81
80
|
}
|
|
82
|
-
/**
|
|
83
|
-
* 移除字符串中的 ASNI Color 修饰符
|
|
84
|
-
* https://stackoverflow.com/a/7150870/2815178
|
|
85
|
-
*/
|
|
86
|
-
export function removeANSIColor(string) {
|
|
87
|
-
return string.replace(
|
|
88
|
-
// eslint-disable-next-line no-control-regex
|
|
89
|
-
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
90
|
-
}
|
package/lang/types.d.ts
CHANGED
|
@@ -62,12 +62,13 @@ export interface Success<T = void> {
|
|
|
62
62
|
export interface Failed<ET = string> {
|
|
63
63
|
success: false;
|
|
64
64
|
error: ET;
|
|
65
|
+
code?: number | string;
|
|
65
66
|
}
|
|
66
67
|
export type MaySuccess<T = void, ET = string> = Success<T> | Failed<ET>;
|
|
67
68
|
declare function success(): Success;
|
|
68
69
|
declare function success<T>(data: T): Success<T>;
|
|
69
70
|
export { success };
|
|
70
|
-
export declare function failed<ET>(error: ET): Failed<ET>;
|
|
71
|
+
export declare function failed<ET>(error: ET, code?: number | string): Failed<ET>;
|
|
71
72
|
/**
|
|
72
73
|
* 若传入值为 success,格式化其 data;否则原样返回错误
|
|
73
74
|
* 支持传入会返回 MaySuccess 的 Promise
|
package/lang/types.js
CHANGED
|
@@ -18,8 +18,8 @@ function success(data) {
|
|
|
18
18
|
return { success: true, data };
|
|
19
19
|
}
|
|
20
20
|
export { success };
|
|
21
|
-
export function failed(error) {
|
|
22
|
-
return { success: false, error };
|
|
21
|
+
export function failed(error, code) {
|
|
22
|
+
return { success: false, error, code };
|
|
23
23
|
}
|
|
24
24
|
function formatSuccess(item, formatter) {
|
|
25
25
|
if ('then' in item)
|
package/logging/adapt.d.ts
CHANGED
package/logging/adapt.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getLogger } from './index.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* 适配 debug package
|
|
4
4
|
*/
|
|
5
|
-
export function adaptDebugLib(debugLib, enable = ''
|
|
5
|
+
export function adaptDebugLib(debugLib, enable = '') {
|
|
6
6
|
// 不在 localStorage 里记录 debugLib enable 状态,
|
|
7
7
|
// 以解决 web worker 里读不到 localStorage 而无法启用 debugLib 日志的问题
|
|
8
8
|
const emulate = {
|
|
@@ -30,12 +30,8 @@ export function adaptDebugLib(debugLib, enable = '', logger = getLogger('3rd-lib
|
|
|
30
30
|
};
|
|
31
31
|
Object.assign(debugLib, emulate);
|
|
32
32
|
// 将 debugLib 日志转发给 logger
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (typeof initial === 'string')
|
|
36
|
-
initial = initial.trimStart();
|
|
37
|
-
logger.debug(initial, ...rest);
|
|
38
|
-
};
|
|
33
|
+
const logger = getLogger('3rd-library');
|
|
34
|
+
debugLib.log = logger.debug.bind(logger);
|
|
39
35
|
if (enable) {
|
|
40
36
|
debugLib.enable(enable);
|
|
41
37
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anjianshi/utils",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Common JavaScript Utils",
|
|
5
5
|
"homepage": "https://github.com/anjianshi/js-utils",
|
|
6
6
|
"bugs": {
|
|
@@ -25,20 +25,19 @@
|
|
|
25
25
|
},
|
|
26
26
|
"main": "index.js",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"chalk": "^5.3.0",
|
|
29
28
|
"dayjs": "^1.11.10",
|
|
30
29
|
"lodash": "^4.17.21"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
|
-
"@anjianshi/presets": "1.
|
|
32
|
+
"@anjianshi/presets-eslint-node": "^1.0.3",
|
|
33
|
+
"@anjianshi/presets-eslint-typescript": "^1.0.3",
|
|
34
|
+
"@anjianshi/presets-prettier": "^1.0.0",
|
|
35
|
+
"@anjianshi/presets-typescript": "^1.0.1",
|
|
34
36
|
"@types/debug": "^4.1.9",
|
|
35
37
|
"@types/lodash": "^4.14.199",
|
|
36
38
|
"@types/node": "^20.8.6",
|
|
37
|
-
"eslint": "^8.51.0",
|
|
38
|
-
"prettier-not-stubborn": "^3.0.3",
|
|
39
|
-
"typescript": "^5.3.2",
|
|
40
39
|
"vconsole": "^3.15.1"
|
|
41
40
|
},
|
|
42
41
|
"eslintIgnore": [],
|
|
43
|
-
"prettier": "@anjianshi/presets/prettierrc"
|
|
42
|
+
"prettier": "@anjianshi/presets-prettier/prettierrc"
|
|
44
43
|
}
|
package/src/.eslintrc.cjs
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
/* eslint-env node */
|
|
2
2
|
module.exports = {
|
|
3
|
-
|
|
4
|
-
extends: [
|
|
5
|
-
'../node_modules/@anjianshi/presets/eslint-base.js',
|
|
6
|
-
'../node_modules/@anjianshi/presets/eslint-typescript.js',
|
|
7
|
-
],
|
|
3
|
+
extends: ['../node_modules/@anjianshi/presets-eslint-typescript/exclusive.js'],
|
|
8
4
|
}
|
package/src/env-node/logging.ts
CHANGED
|
@@ -7,7 +7,6 @@ import path from 'node:path'
|
|
|
7
7
|
import { fileURLToPath } from 'node:url'
|
|
8
8
|
import chalk from 'chalk'
|
|
9
9
|
import dayjs from 'dayjs'
|
|
10
|
-
import { removeANSIColor } from '../lang/index.js'
|
|
11
10
|
import {
|
|
12
11
|
logger as defaultLogger,
|
|
13
12
|
type Logger,
|
|
@@ -30,9 +29,9 @@ export class ConsoleHandler extends LogHandler {
|
|
|
30
29
|
const levelName = formatters.level(info)
|
|
31
30
|
const loggerColor = chalk[ConsoleHandler.getLoggerColor(logger)]
|
|
32
31
|
const prefix = [
|
|
33
|
-
chalk.white(`[${formatters.
|
|
32
|
+
chalk.white(`[${formatters.time(info)}]`),
|
|
34
33
|
levelColor(`[${levelName}]`),
|
|
35
|
-
|
|
34
|
+
loggerColor(`[${logger}]`),
|
|
36
35
|
].join('')
|
|
37
36
|
method(prefix, ...args)
|
|
38
37
|
}
|
|
@@ -121,7 +120,7 @@ export class FileHandler extends LogHandler {
|
|
|
121
120
|
const itemStrings: string[] = []
|
|
122
121
|
let totalLength = prefix.length
|
|
123
122
|
for (const item of args) {
|
|
124
|
-
const itemString = this.
|
|
123
|
+
const itemString = this.stringifyDataItem(item)
|
|
125
124
|
|
|
126
125
|
// 截断过长的日志内容 Truncate overly long log messages
|
|
127
126
|
if (totalLength + itemString.length < this.options.maxLength) {
|
|
@@ -138,8 +137,8 @@ export class FileHandler extends LogHandler {
|
|
|
138
137
|
this.pushBuffer(prefix, ...itemStrings, '\n')
|
|
139
138
|
}
|
|
140
139
|
|
|
141
|
-
protected
|
|
142
|
-
if (typeof item === 'string') return
|
|
140
|
+
protected stringifyDataItem(item: unknown) {
|
|
141
|
+
if (typeof item === 'string') return item
|
|
143
142
|
if (item instanceof Error) return item.stack ?? String(item)
|
|
144
143
|
try {
|
|
145
144
|
const json = JSON.stringify(item) as string | undefined
|
package/src/init-dayjs.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { extend, locale } from 'dayjs'
|
|
2
2
|
import objectSupport from 'dayjs/plugin/objectSupport.js'
|
|
3
3
|
import 'dayjs/locale/zh-cn.js'
|
|
4
4
|
|
|
5
5
|
export function initDayJs() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
dayjs.locale('zh-cn')
|
|
6
|
+
extend(objectSupport)
|
|
7
|
+
locale('zh-cn')
|
|
9
8
|
}
|
package/src/lang/string.ts
CHANGED
|
@@ -32,7 +32,6 @@ export function keywordCompare(keyword: string, target: string) {
|
|
|
32
32
|
*
|
|
33
33
|
* 以 0 开头是特例,例如 019 和 12 两个字符串,按直觉还是应该 019 在前面,毕竟本质还是字符串排序。
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
35
|
* "123" "456" 数字排序
|
|
37
36
|
* "123你好" "133我好" 字符串排序
|
|
38
37
|
* "019" "12" 字符串排序
|
|
@@ -62,11 +61,11 @@ export function safeParseFloat(value: string | number, fallback: number) {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
64
|
+
* 返回人类可读的文件尺寸
|
|
65
|
+
* 来自:https://stackoverflow.com/a/14919494
|
|
67
66
|
*
|
|
68
|
-
* si: true
|
|
69
|
-
* dp:
|
|
67
|
+
* si: true 则使用 1000 进制,否则 1024 进制(默认 false)
|
|
68
|
+
* dp: 保留几位小数
|
|
70
69
|
*/
|
|
71
70
|
export function readableSize(bytes: number, si = false, dp = 1) {
|
|
72
71
|
const thresh = si ? 1000 : 1024
|
|
@@ -82,15 +81,3 @@ export function readableSize(bytes: number, si = false, dp = 1) {
|
|
|
82
81
|
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1)
|
|
83
82
|
return `${bytes.toFixed(dp)} ${units[u]!}`
|
|
84
83
|
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 移除字符串中的 ASNI Color 修饰符
|
|
88
|
-
* https://stackoverflow.com/a/7150870/2815178
|
|
89
|
-
*/
|
|
90
|
-
export function removeANSIColor(string: string) {
|
|
91
|
-
return string.replace(
|
|
92
|
-
// eslint-disable-next-line no-control-regex
|
|
93
|
-
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
|
94
|
-
'',
|
|
95
|
-
)
|
|
96
|
-
}
|
package/src/lang/types.ts
CHANGED
|
@@ -73,6 +73,7 @@ export interface Success<T = void> {
|
|
|
73
73
|
export interface Failed<ET = string> {
|
|
74
74
|
success: false
|
|
75
75
|
error: ET
|
|
76
|
+
code?: number | string
|
|
76
77
|
}
|
|
77
78
|
export type MaySuccess<T = void, ET = string> = Success<T> | Failed<ET>
|
|
78
79
|
|
|
@@ -83,25 +84,19 @@ function success<T = void>(data?: T) {
|
|
|
83
84
|
}
|
|
84
85
|
export { success }
|
|
85
86
|
|
|
86
|
-
export function failed<ET>(error: ET): Failed<ET> {
|
|
87
|
-
return { success: false, error }
|
|
87
|
+
export function failed<ET>(error: ET, code?: number | string): Failed<ET> {
|
|
88
|
+
return { success: false, error, code }
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
/**
|
|
91
92
|
* 若传入值为 success,格式化其 data;否则原样返回错误
|
|
92
93
|
* 支持传入会返回 MaySuccess 的 Promise
|
|
93
94
|
*/
|
|
94
|
-
function formatSuccess<T, ET, FT>(
|
|
95
|
-
|
|
96
|
-
formatter: (data: T) => FT,
|
|
97
|
-
): MaySuccess<FT, ET>
|
|
98
|
-
function formatSuccess<T, ET, FT>(
|
|
99
|
-
item: Promise<MaySuccess<T, ET>>,
|
|
100
|
-
formatter: (data: T) => FT,
|
|
101
|
-
): Promise<MaySuccess<FT, ET>>
|
|
95
|
+
function formatSuccess<T, ET, FT>(item: MaySuccess<T, ET>, formatter: (data: T) => FT): MaySuccess<FT, ET> // prettier-ignore
|
|
96
|
+
function formatSuccess<T, ET, FT>(item: Promise<MaySuccess<T, ET>>, formatter: (data: T) => FT): Promise<MaySuccess<FT, ET>> // prettier-ignore
|
|
102
97
|
function formatSuccess<T, ET, FT>(
|
|
103
98
|
item: MaySuccess<T, ET> | Promise<MaySuccess<T, ET>>,
|
|
104
|
-
formatter: (data: T) => FT
|
|
99
|
+
formatter: (data: T) => FT
|
|
105
100
|
) {
|
|
106
101
|
if ('then' in item) return item.then(finalItem => formatSuccess(finalItem, formatter))
|
|
107
102
|
return item.success ? { ...item, data: formatter(item.data) } : item
|
|
@@ -113,10 +108,10 @@ export { formatSuccess }
|
|
|
113
108
|
* 注意:空字符串和数字 0 也会判定为没有值
|
|
114
109
|
*/
|
|
115
110
|
function truthy(
|
|
116
|
-
value: string | number | boolean | null | undefined
|
|
111
|
+
value: string | number | boolean | null | undefined
|
|
117
112
|
): value is string | number | true
|
|
118
113
|
function truthy<T>(
|
|
119
|
-
value: T | string | number | boolean | null | undefined
|
|
114
|
+
value: T | string | number | boolean | null | undefined
|
|
120
115
|
): value is T | string | number | true
|
|
121
116
|
function truthy<T>(value: T | string | number | boolean | null | undefined) {
|
|
122
117
|
return value !== null && value !== undefined && value !== '' && value !== 0 && value !== false
|
package/src/logging/adapt.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { getLogger } from './index.js'
|
|
|
4
4
|
/**
|
|
5
5
|
* 适配 debug package
|
|
6
6
|
*/
|
|
7
|
-
export function adaptDebugLib(debugLib: Debug, enable = ''
|
|
7
|
+
export function adaptDebugLib(debugLib: Debug, enable = '') {
|
|
8
8
|
// 不在 localStorage 里记录 debugLib enable 状态,
|
|
9
9
|
// 以解决 web worker 里读不到 localStorage 而无法启用 debugLib 日志的问题
|
|
10
10
|
const emulate = {
|
|
@@ -31,11 +31,8 @@ export function adaptDebugLib(debugLib: Debug, enable = '', logger = getLogger('
|
|
|
31
31
|
Object.assign(debugLib, emulate)
|
|
32
32
|
|
|
33
33
|
// 将 debugLib 日志转发给 logger
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (typeof initial === 'string') initial = initial.trimStart()
|
|
37
|
-
logger.debug(initial, ...rest)
|
|
38
|
-
}
|
|
34
|
+
const logger = getLogger('3rd-library')
|
|
35
|
+
debugLib.log = logger.debug.bind(logger)
|
|
39
36
|
|
|
40
37
|
if (enable) {
|
|
41
38
|
debugLib.enable(enable)
|
package/src/logging/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ export enum LogLevel {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface LogInfo {
|
|
17
|
-
logger: string //
|
|
17
|
+
logger: string // logger name;有多级 logger 的情况下,这是最初的 logger 名称
|
|
18
18
|
level: LogLevel
|
|
19
19
|
time: Dayjs
|
|
20
20
|
args: unknown[] // log content
|
package/src/url.ts
CHANGED
|
@@ -10,20 +10,21 @@
|
|
|
10
10
|
import isPlainObject from 'lodash/isPlainObject.js'
|
|
11
11
|
|
|
12
12
|
/*
|
|
13
|
-
|
|
13
|
+
从字符串中解析出 query 对象
|
|
14
14
|
|
|
15
15
|
array:
|
|
16
16
|
- 若开启,支持解析 a[]=1&a[]=2 格式的参数,会解析成一个数组 { a: ['1', '2'] }
|
|
17
17
|
- 否则把 `a[]` 整体当做一个参数名 { 'a[]': '2' }
|
|
18
18
|
|
|
19
19
|
strict:
|
|
20
|
-
|
|
20
|
+
是否开启“严格模式”(默认不开启)。
|
|
21
|
+
在非严格模式下,会做很多兼容处理:
|
|
21
22
|
1. 支持直接传入 query string(a=1&b=2);严格模式下,则需在开头补充一个 ?(?a=1&b=2)
|
|
22
23
|
2. hash 里的内容也会被解析,以兼容拼接错误的 URL(把 query 拼到了 hash 后面)。
|
|
23
24
|
3. 出现多个 ? 符号时,会把 ? 也当做 & 分隔符(index.html?a=1&b=2?c=3)
|
|
24
25
|
|
|
25
26
|
小技巧:
|
|
26
|
-
|
|
27
|
+
在非严格模式下,如果明确只想解析 hash 或 search 里的内容,可以传入 location.search / hash 而不是传入整个 location.href
|
|
27
28
|
|
|
28
29
|
此函数不会对 query 值进行 decode,需自定处理
|
|
29
30
|
*/
|
|
@@ -31,7 +32,7 @@ function parseQuery(url: string, options?: { array?: false, strict?: boolean }):
|
|
|
31
32
|
function parseQuery(url: string, options: { array: true, strict?: boolean }): Record<string, string | string[]> // prettier-ignore
|
|
32
33
|
function parseQuery(
|
|
33
34
|
url: string,
|
|
34
|
-
options?: { array?: boolean; strict?: boolean }
|
|
35
|
+
options?: { array?: boolean; strict?: boolean }
|
|
35
36
|
): Record<string, string | string[]> {
|
|
36
37
|
if (!url) return {}
|
|
37
38
|
const { array = false, strict = false } = options ?? {}
|
|
@@ -58,21 +59,21 @@ function parseQuery(
|
|
|
58
59
|
}
|
|
59
60
|
export { parseQuery }
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
取 query 中指定参数的值
|
|
63
|
-
- 参数存在,返回参数值(可能是空字符串);不存在返回 null
|
|
64
|
-
- 解析 query 固定基于 parseQuery() 的 { array: false, strict: false } 规则
|
|
65
|
-
- 和 parseQuery() 一样,url 可以根据需要传 location.href/search/hash
|
|
66
|
-
*/
|
|
62
|
+
/**
|
|
63
|
+
* 取 query 中指定参数的值
|
|
64
|
+
* - 参数存在,返回参数值(可能是空字符串);不存在返回 null
|
|
65
|
+
* - 解析 query 固定基于 parseQuery() 的 { array: false, strict: false } 规则
|
|
66
|
+
* - 和 parseQuery() 一样,url 可以根据需要传 location.href/search/hash
|
|
67
|
+
*/
|
|
67
68
|
export function getQueryParam(name: string, url: string): string | null {
|
|
68
69
|
const query = parseQuery(url)
|
|
69
70
|
return typeof query[name] === 'string' ? query[name]! : null
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
把对象合并成 query string。
|
|
74
|
-
支持字符串、数值、布尔值(不建议,考虑用 0 和 1 代替),数组会转换成 name[]=value 的格式
|
|
75
|
-
*/
|
|
73
|
+
/**
|
|
74
|
+
* 把对象合并成 query string。
|
|
75
|
+
* 支持字符串、数值、布尔值(不建议,考虑用 0 和 1 代替),数组会转换成 name[]=value 的格式
|
|
76
|
+
*/
|
|
76
77
|
type StringifyVal = string | number | boolean
|
|
77
78
|
export function stringifyQuery(obj: { [key: string]: StringifyVal | StringifyVal[] | undefined }) {
|
|
78
79
|
if (!isPlainObject(obj)) return ''
|
|
@@ -91,52 +92,56 @@ function stringifyQueryItem(name: string, value: StringifyVal | StringifyVal[]):
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/**
|
|
94
|
-
* 拆分 URL
|
|
95
|
+
* 拆分 URL 的各个部分
|
|
96
|
+
*
|
|
97
|
+
* bare 为 true,则 search 不带 '?',hash 不带 '#'
|
|
98
|
+
* 否则和 location.search / hash 一样
|
|
95
99
|
*/
|
|
96
|
-
export function splitUrl(url: string): { base: string; search: string; hash: string } {
|
|
100
|
+
export function splitUrl(url: string, bare = true): { base: string; search: string; hash: string } {
|
|
97
101
|
let hashIndex = url.indexOf('#')
|
|
98
102
|
if (hashIndex === -1) hashIndex = url.length
|
|
103
|
+
const bareHash = url.slice(hashIndex + 1)
|
|
99
104
|
|
|
100
105
|
let searchIndex = url.slice(0, hashIndex).indexOf('?')
|
|
101
106
|
if (searchIndex === -1) searchIndex = hashIndex
|
|
107
|
+
const bareSearch = url.slice(searchIndex + 1, hashIndex)
|
|
102
108
|
|
|
103
109
|
return {
|
|
104
110
|
base: url.slice(0, searchIndex),
|
|
105
|
-
search:
|
|
106
|
-
hash:
|
|
111
|
+
search: bare ? bareSearch : bareSearch ? '?' + bareSearch : '',
|
|
112
|
+
hash: bare ? bareHash : bareHash ? '#' + bareHash : '',
|
|
107
113
|
}
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
|
|
111
|
-
把指定
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
hash string
|
|
115
|
-
*/
|
|
116
|
+
/**
|
|
117
|
+
* 把指定 query 和 hash 内容合并到 url 上
|
|
118
|
+
*
|
|
119
|
+
* query object 与现有 search 合并,替换同名项(值为数组的,用新数组代替老的,不会合并数组)
|
|
120
|
+
* hash string 若 url 已有 hash,会用此值代替。带不带开头的 '?' 皆可。
|
|
121
|
+
*/
|
|
116
122
|
export function combineUrl(
|
|
117
123
|
origUrl: string,
|
|
118
|
-
|
|
119
|
-
hash
|
|
124
|
+
query: Record<string, string | string[]> = {},
|
|
125
|
+
hash: string = ''
|
|
120
126
|
) {
|
|
121
|
-
|
|
122
|
-
hash = hash ?? ''
|
|
127
|
+
if (hash.startsWith('#')) hash = hash.slice(1)
|
|
123
128
|
|
|
124
129
|
// 拆分原 url 的 search、hash
|
|
125
130
|
const { base, search: origSearch, hash: origHash } = splitUrl(origUrl)
|
|
126
131
|
|
|
127
132
|
// 拼接新 URL
|
|
128
133
|
let newUrl = base
|
|
129
|
-
const newSearch = stringifyQuery({ ...parseQuery(origSearch), ...
|
|
134
|
+
const newSearch = stringifyQuery({ ...parseQuery(origSearch), ...query })
|
|
130
135
|
const newHash = hash || origHash
|
|
131
136
|
if (newSearch) newUrl += `?${newSearch}`
|
|
132
137
|
if (newHash) newUrl += `#${newHash}`
|
|
133
138
|
return newUrl
|
|
134
139
|
}
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
decodeURIComponent() 对于不规范编码的字符串可能会报错(例如字符串里出现了“%”)
|
|
138
|
-
用此函数代替可避免此问题
|
|
139
|
-
*/
|
|
141
|
+
/**
|
|
142
|
+
* decodeURIComponent() 对于不规范编码的字符串可能会报错(例如字符串里出现了“%”)
|
|
143
|
+
* 用此函数代替可避免此问题
|
|
144
|
+
*/
|
|
140
145
|
export function safeDecode(content: string) {
|
|
141
146
|
try {
|
|
142
147
|
return decodeURIComponent(content)
|
package/url.d.ts
CHANGED
|
@@ -7,20 +7,43 @@ declare function parseQuery(url: string, options: {
|
|
|
7
7
|
strict?: boolean;
|
|
8
8
|
}): Record<string, string | string[]>;
|
|
9
9
|
export { parseQuery };
|
|
10
|
+
/**
|
|
11
|
+
* 取 query 中指定参数的值
|
|
12
|
+
* - 参数存在,返回参数值(可能是空字符串);不存在返回 null
|
|
13
|
+
* - 解析 query 固定基于 parseQuery() 的 { array: false, strict: false } 规则
|
|
14
|
+
* - 和 parseQuery() 一样,url 可以根据需要传 location.href/search/hash
|
|
15
|
+
*/
|
|
10
16
|
export declare function getQueryParam(name: string, url: string): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* 把对象合并成 query string。
|
|
19
|
+
* 支持字符串、数值、布尔值(不建议,考虑用 0 和 1 代替),数组会转换成 name[]=value 的格式
|
|
20
|
+
*/
|
|
11
21
|
type StringifyVal = string | number | boolean;
|
|
12
22
|
export declare function stringifyQuery(obj: {
|
|
13
23
|
[key: string]: StringifyVal | StringifyVal[] | undefined;
|
|
14
24
|
}): string;
|
|
15
25
|
/**
|
|
16
|
-
* 拆分 URL
|
|
26
|
+
* 拆分 URL 的各个部分
|
|
27
|
+
*
|
|
28
|
+
* bare 为 true,则 search 不带 '?',hash 不带 '#'
|
|
29
|
+
* 否则和 location.search / hash 一样
|
|
17
30
|
*/
|
|
18
|
-
export declare function splitUrl(url: string): {
|
|
31
|
+
export declare function splitUrl(url: string, bare?: boolean): {
|
|
19
32
|
base: string;
|
|
20
33
|
search: string;
|
|
21
34
|
hash: string;
|
|
22
35
|
};
|
|
23
|
-
|
|
36
|
+
/**
|
|
37
|
+
* 把指定 query 和 hash 内容合并到 url 上
|
|
38
|
+
*
|
|
39
|
+
* query object 与现有 search 合并,替换同名项(值为数组的,用新数组代替老的,不会合并数组)
|
|
40
|
+
* hash string 若 url 已有 hash,会用此值代替。带不带开头的 '?' 皆可。
|
|
41
|
+
*/
|
|
42
|
+
export declare function combineUrl(origUrl: string, query?: Record<string, string | string[]>, hash?: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* decodeURIComponent() 对于不规范编码的字符串可能会报错(例如字符串里出现了“%”)
|
|
45
|
+
* 用此函数代替可避免此问题
|
|
46
|
+
*/
|
|
24
47
|
export declare function safeDecode(content: string): string;
|
|
25
48
|
/**
|
|
26
49
|
* 将 URL 中的 http:// 协议改成 https://
|
package/url.js
CHANGED
|
@@ -35,12 +35,12 @@ function parseQuery(url, options) {
|
|
|
35
35
|
return query;
|
|
36
36
|
}
|
|
37
37
|
export { parseQuery };
|
|
38
|
-
|
|
39
|
-
取 query 中指定参数的值
|
|
40
|
-
- 参数存在,返回参数值(可能是空字符串);不存在返回 null
|
|
41
|
-
- 解析 query 固定基于 parseQuery() 的 { array: false, strict: false } 规则
|
|
42
|
-
- 和 parseQuery() 一样,url 可以根据需要传 location.href/search/hash
|
|
43
|
-
*/
|
|
38
|
+
/**
|
|
39
|
+
* 取 query 中指定参数的值
|
|
40
|
+
* - 参数存在,返回参数值(可能是空字符串);不存在返回 null
|
|
41
|
+
* - 解析 query 固定基于 parseQuery() 的 { array: false, strict: false } 规则
|
|
42
|
+
* - 和 parseQuery() 一样,url 可以根据需要传 location.href/search/hash
|
|
43
|
+
*/
|
|
44
44
|
export function getQueryParam(name, url) {
|
|
45
45
|
const query = parseQuery(url);
|
|
46
46
|
return typeof query[name] === 'string' ? query[name] : null;
|
|
@@ -60,35 +60,40 @@ function stringifyQueryItem(name, value) {
|
|
|
60
60
|
return `${name}=${value}`;
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
* 拆分 URL
|
|
63
|
+
* 拆分 URL 的各个部分
|
|
64
|
+
*
|
|
65
|
+
* bare 为 true,则 search 不带 '?',hash 不带 '#'
|
|
66
|
+
* 否则和 location.search / hash 一样
|
|
64
67
|
*/
|
|
65
|
-
export function splitUrl(url) {
|
|
68
|
+
export function splitUrl(url, bare = true) {
|
|
66
69
|
let hashIndex = url.indexOf('#');
|
|
67
70
|
if (hashIndex === -1)
|
|
68
71
|
hashIndex = url.length;
|
|
72
|
+
const bareHash = url.slice(hashIndex + 1);
|
|
69
73
|
let searchIndex = url.slice(0, hashIndex).indexOf('?');
|
|
70
74
|
if (searchIndex === -1)
|
|
71
75
|
searchIndex = hashIndex;
|
|
76
|
+
const bareSearch = url.slice(searchIndex + 1, hashIndex);
|
|
72
77
|
return {
|
|
73
78
|
base: url.slice(0, searchIndex),
|
|
74
|
-
search:
|
|
75
|
-
hash:
|
|
79
|
+
search: bare ? bareSearch : bareSearch ? '?' + bareSearch : '',
|
|
80
|
+
hash: bare ? bareHash : bareHash ? '#' + bareHash : '',
|
|
76
81
|
};
|
|
77
82
|
}
|
|
78
|
-
|
|
79
|
-
把指定
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
hash string
|
|
83
|
-
*/
|
|
84
|
-
export function combineUrl(origUrl,
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
/**
|
|
84
|
+
* 把指定 query 和 hash 内容合并到 url 上
|
|
85
|
+
*
|
|
86
|
+
* query object 与现有 search 合并,替换同名项(值为数组的,用新数组代替老的,不会合并数组)
|
|
87
|
+
* hash string 若 url 已有 hash,会用此值代替。带不带开头的 '?' 皆可。
|
|
88
|
+
*/
|
|
89
|
+
export function combineUrl(origUrl, query = {}, hash = '') {
|
|
90
|
+
if (hash.startsWith('#'))
|
|
91
|
+
hash = hash.slice(1);
|
|
87
92
|
// 拆分原 url 的 search、hash
|
|
88
93
|
const { base, search: origSearch, hash: origHash } = splitUrl(origUrl);
|
|
89
94
|
// 拼接新 URL
|
|
90
95
|
let newUrl = base;
|
|
91
|
-
const newSearch = stringifyQuery({ ...parseQuery(origSearch), ...
|
|
96
|
+
const newSearch = stringifyQuery({ ...parseQuery(origSearch), ...query });
|
|
92
97
|
const newHash = hash || origHash;
|
|
93
98
|
if (newSearch)
|
|
94
99
|
newUrl += `?${newSearch}`;
|
|
@@ -96,10 +101,10 @@ export function combineUrl(origUrl, search, hash) {
|
|
|
96
101
|
newUrl += `#${newHash}`;
|
|
97
102
|
return newUrl;
|
|
98
103
|
}
|
|
99
|
-
|
|
100
|
-
decodeURIComponent() 对于不规范编码的字符串可能会报错(例如字符串里出现了“%”)
|
|
101
|
-
用此函数代替可避免此问题
|
|
102
|
-
*/
|
|
104
|
+
/**
|
|
105
|
+
* decodeURIComponent() 对于不规范编码的字符串可能会报错(例如字符串里出现了“%”)
|
|
106
|
+
* 用此函数代替可避免此问题
|
|
107
|
+
*/
|
|
103
108
|
export function safeDecode(content) {
|
|
104
109
|
try {
|
|
105
110
|
return decodeURIComponent(content);
|