@anjianshi/utils 3.0.0 → 3.0.2
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/env-browser/device.d.ts +24 -0
- package/env-browser/device.js +50 -0
- package/env-browser/global.d.ts +10 -0
- package/env-browser/global.js +15 -0
- package/env-browser/load-script.d.ts +5 -0
- package/env-browser/load-script.js +13 -0
- package/env-browser/logging.d.ts +18 -0
- package/env-browser/logging.js +49 -0
- package/env-browser/manage-vconsole.d.ts +16 -0
- package/env-browser/manage-vconsole.js +38 -0
- package/env-node/crypto-random.d.ts +13 -0
- package/env-node/crypto-random.js +28 -0
- package/env-node/fs.d.ts +19 -0
- package/env-node/fs.js +48 -0
- package/env-node/index.d.ts +5 -0
- package/env-node/index.js +5 -0
- package/env-node/logging/handlers.d.ts +58 -0
- package/env-node/logging/handlers.js +154 -0
- package/env-node/logging/index.d.ts +11 -0
- package/env-node/logging/index.js +14 -0
- package/{src/env-react/emotion-register-globals.ts → env-react/emotion-register-globals.d.ts} +2 -5
- package/env-react/emotion-register-globals.js +5 -0
- package/env-react/emotion.d.ts +20 -0
- package/env-react/emotion.jsx +34 -0
- package/env-react/hooks.d.ts +23 -0
- package/env-react/hooks.js +47 -0
- package/env-react/index.d.ts +1 -0
- package/env-react/index.js +1 -0
- package/env-react/react-register-globals.d.ts +21 -0
- package/env-react/react-register-globals.js +19 -0
- package/env-service/controllers.d.ts +30 -0
- package/env-service/controllers.js +41 -0
- package/env-service/env-reader.d.ts +55 -0
- package/env-service/env-reader.js +79 -0
- package/env-service/index.d.ts +6 -0
- package/env-service/index.js +6 -0
- package/env-service/prisma/adapt-logging.d.ts +21 -0
- package/env-service/prisma/adapt-logging.js +30 -0
- package/env-service/prisma/extensions/exist.d.ts +10 -0
- package/env-service/prisma/extensions/exist.js +16 -0
- package/env-service/prisma/extensions/find-and-count.d.ts +7 -0
- package/env-service/prisma/extensions/find-and-count.js +19 -0
- package/env-service/prisma/extensions/soft-delete.d.ts +52 -0
- package/env-service/prisma/extensions/soft-delete.js +123 -0
- package/env-service/prisma/extensions/with-transaction.d.ts +9 -0
- package/env-service/prisma/extensions/with-transaction.js +54 -0
- package/env-service/prisma/index.d.ts +6 -0
- package/env-service/prisma/index.js +6 -0
- package/env-service/prisma/transaction-contexted.d.ts +11 -0
- package/env-service/prisma/transaction-contexted.js +52 -0
- package/env-service/redis-cache.d.ts +39 -0
- package/env-service/redis-cache.js +116 -0
- package/env-service/tasks.d.ts +12 -0
- package/env-service/tasks.js +37 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/init-dayjs.d.ts +2 -0
- package/init-dayjs.js +7 -0
- package/lang/async.d.ts +19 -0
- package/lang/async.js +34 -0
- package/lang/color.d.ts +37 -0
- package/lang/color.js +111 -0
- package/lang/index.d.ts +8 -0
- package/lang/index.js +8 -0
- package/lang/object.d.ts +12 -0
- package/lang/object.js +41 -0
- package/lang/random.d.ts +13 -0
- package/lang/random.js +24 -0
- package/lang/result.d.ts +47 -0
- package/lang/result.js +45 -0
- package/lang/string.d.ts +29 -0
- package/lang/string.js +92 -0
- package/lang/time.d.ts +10 -0
- package/lang/time.js +18 -0
- package/{src/lang/types.ts → lang/types.d.ts} +23 -43
- package/lang/types.js +28 -0
- package/logging/adapt.d.ts +10 -0
- package/logging/adapt.js +43 -0
- package/logging/formatters.d.ts +10 -0
- package/logging/formatters.js +22 -0
- package/logging/index.d.ts +45 -0
- package/logging/index.js +90 -0
- package/md5.d.ts +30 -0
- package/md5.js +308 -0
- package/package.json +10 -19
- package/safe-request.d.ts +53 -0
- package/safe-request.js +140 -0
- package/url.d.ts +77 -0
- package/url.js +149 -0
- package/validators/array.d.ts +30 -0
- package/validators/array.js +47 -0
- package/validators/base.d.ts +82 -0
- package/validators/base.js +42 -0
- package/validators/boolean.d.ts +3 -0
- package/validators/boolean.js +22 -0
- package/validators/datetime.d.ts +12 -0
- package/validators/datetime.js +30 -0
- package/validators/factory.d.ts +70 -0
- package/validators/factory.js +121 -0
- package/validators/index.d.ts +9 -0
- package/validators/index.js +9 -0
- package/validators/number.d.ts +19 -0
- package/validators/number.js +26 -0
- package/validators/object.d.ts +28 -0
- package/validators/object.js +49 -0
- package/validators/one-of.d.ts +10 -0
- package/validators/one-of.js +15 -0
- package/validators/string.d.ts +22 -0
- package/validators/string.js +35 -0
- package/README.md +0 -10
- package/eslint.config.cjs +0 -33
- package/publish-prepare.cjs +0 -16
- package/src/env-browser/device.ts +0 -62
- package/src/env-browser/global.ts +0 -21
- package/src/env-browser/load-script.ts +0 -13
- package/src/env-browser/logging.ts +0 -58
- package/src/env-browser/manage-vconsole.ts +0 -54
- package/src/env-node/crypto-random.ts +0 -30
- package/src/env-node/fs.ts +0 -50
- package/src/env-node/index.ts +0 -5
- package/src/env-node/logging/handlers.ts +0 -190
- package/src/env-node/logging/index.ts +0 -16
- package/src/env-react/emotion.tsx +0 -42
- package/src/env-react/hooks.ts +0 -59
- package/src/env-react/index.ts +0 -1
- package/src/env-react/react-register-globals.ts +0 -53
- package/src/env-service/controllers.ts +0 -93
- package/src/env-service/env-reader.ts +0 -141
- package/src/env-service/index.ts +0 -6
- package/src/env-service/prisma/adapt-logging.ts +0 -39
- package/src/env-service/prisma/extensions/exist.ts +0 -21
- package/src/env-service/prisma/extensions/find-and-count.ts +0 -24
- package/src/env-service/prisma/extensions/soft-delete.ts +0 -162
- package/src/env-service/prisma/extensions/with-transaction.ts +0 -65
- package/src/env-service/prisma/index.ts +0 -6
- package/src/env-service/prisma/transaction-contexted.ts +0 -80
- package/src/env-service/redis-cache.ts +0 -142
- package/src/env-service/tasks.ts +0 -45
- package/src/index.ts +0 -4
- package/src/init-dayjs.ts +0 -8
- package/src/lang/async.ts +0 -47
- package/src/lang/color.ts +0 -119
- package/src/lang/index.ts +0 -8
- package/src/lang/object.ts +0 -39
- package/src/lang/random.ts +0 -25
- package/src/lang/result.ts +0 -78
- package/src/lang/string.ts +0 -95
- package/src/lang/time.ts +0 -19
- package/src/logging/adapt.ts +0 -49
- package/src/logging/formatters.ts +0 -23
- package/src/logging/index.ts +0 -106
- package/src/md5.ts +0 -318
- package/src/safe-request.ts +0 -193
- package/src/url.ts +0 -185
- package/src/validators/array.ts +0 -97
- package/src/validators/base.ts +0 -145
- package/src/validators/boolean.ts +0 -21
- package/src/validators/datetime.ts +0 -39
- package/src/validators/factory.ts +0 -244
- package/src/validators/index.ts +0 -9
- package/src/validators/number.ts +0 -54
- package/src/validators/object.ts +0 -101
- package/src/validators/one-of.ts +0 -33
- package/src/validators/string.ts +0 -72
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { type default as VConsole } from 'vconsole'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 注册 VConsole 代码
|
|
5
|
-
* 此类库自己并不加载 VConsole,需使用者自行通过依赖或者 CDN 加载 VConsole 然后传给此类库
|
|
6
|
-
*/
|
|
7
|
-
let VConsoleLib: (new () => VConsole) | undefined
|
|
8
|
-
|
|
9
|
-
export function registerVConsoleLib(lib: new () => VConsole) {
|
|
10
|
-
VConsoleLib = lib
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 管理 VConsole 实例
|
|
15
|
-
*/
|
|
16
|
-
declare global {
|
|
17
|
-
interface Window {
|
|
18
|
-
VConsole: VConsole | null
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
window.VConsole = null
|
|
23
|
-
|
|
24
|
-
export function isVConsoleEnabled() {
|
|
25
|
-
return localStorage.getItem('vconsole') === '1'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function detectVConsole() {
|
|
29
|
-
if (isVConsoleEnabled()) {
|
|
30
|
-
runVConsole()
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function enableVConsole() {
|
|
35
|
-
localStorage.setItem('vconsole', '1')
|
|
36
|
-
runVConsole()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function disableVConsole() {
|
|
40
|
-
localStorage.setItem('vconsole', '0')
|
|
41
|
-
destoryVConsole()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function runVConsole() {
|
|
45
|
-
if (window.VConsole !== null) return
|
|
46
|
-
if (VConsoleLib === undefined) return console.warn('尚未传入 VConsole 对象,无法启动')
|
|
47
|
-
window.VConsole = new VConsoleLib()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function destoryVConsole() {
|
|
51
|
-
if (!window.VConsole) return
|
|
52
|
-
window.VConsole.destroy()
|
|
53
|
-
window.VConsole = null
|
|
54
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 类似 lang/random.ts,但基于 Node.js 的 crypto 模块,提供密码级的随机数。
|
|
3
|
-
*/
|
|
4
|
-
import crypto from 'node:crypto'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 返回随机数,包含 min 和 max
|
|
8
|
-
*/
|
|
9
|
-
export function getCryptoRandomInt(min: number, max: number) {
|
|
10
|
-
// 如果传入的 max 小于 min,把它拉到和 min 一样。不然 crypto.randomInt 无法处理
|
|
11
|
-
const fixedMax = Math.max(min, max)
|
|
12
|
-
return crypto.randomInt(min, fixedMax + 1)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 返回随机字符串
|
|
17
|
-
*/
|
|
18
|
-
export function getCryptoRandomString(length = 6, seed = '0123456789abcdefghijklmnopqrstuvwxyz') {
|
|
19
|
-
let result = ''
|
|
20
|
-
while (result.length < length) result += seed[getCryptoRandomInt(0, seed.length - 1)]!
|
|
21
|
-
return result
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 从给定的选择中随机选中一项
|
|
26
|
-
* 如果数组为空,会返回 undefined
|
|
27
|
-
*/
|
|
28
|
-
export function cryptoChoiceRandom<T>(choices: T[]) {
|
|
29
|
-
return choices[getCryptoRandomInt(0, choices.length - 1)]
|
|
30
|
-
}
|
package/src/env-node/fs.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import * as path from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 确认一个路径是否存在且是文件
|
|
7
|
-
*/
|
|
8
|
-
export async function isFileExists(filepath: string) {
|
|
9
|
-
try {
|
|
10
|
-
const res = await fs.promises.stat(filepath)
|
|
11
|
-
return res.isFile()
|
|
12
|
-
} catch (e) {
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 确认一个路径是否存在且是文件夹
|
|
19
|
-
*/
|
|
20
|
-
export async function isDirectoryExists(dirpath: string) {
|
|
21
|
-
try {
|
|
22
|
-
const res = await fs.promises.stat(dirpath)
|
|
23
|
-
return res.isDirectory()
|
|
24
|
-
} catch (e) {
|
|
25
|
-
return false
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 返回当前文件的绝对路径
|
|
31
|
-
* 需要传入当前文件的 ImportMeta 对象(可通过 import.meta 取得)
|
|
32
|
-
*/
|
|
33
|
-
export function getFilePath(fileUrl: string | ImportMeta) {
|
|
34
|
-
if (typeof fileUrl !== 'string') fileUrl = fileUrl.url
|
|
35
|
-
return fileURLToPath(new URL(fileUrl))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 返回文件所处目录的绝对路径
|
|
40
|
-
*/
|
|
41
|
-
export function getDirectoryPath(fileUrl: string | ImportMeta) {
|
|
42
|
-
return path.dirname(getFilePath(fileUrl))
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** 确保目录存在,如果不存在就创建(会递归创建上级目录) */
|
|
46
|
-
export async function mkdirp(dirpath: string, mode?: number | string) {
|
|
47
|
-
if (!(await isDirectoryExists(dirpath))) {
|
|
48
|
-
await fs.promises.mkdir(dirpath, { recursive: true, mode })
|
|
49
|
-
}
|
|
50
|
-
}
|
package/src/env-node/index.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import util from 'node:util'
|
|
5
|
-
import chalk from 'chalk'
|
|
6
|
-
import dayjs from 'dayjs'
|
|
7
|
-
import { type LogInfo, LogLevel, LogHandler, formatters } from '../../logging/index.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 向 console 输出日志
|
|
11
|
-
*/
|
|
12
|
-
export class ConsoleHandler extends LogHandler {
|
|
13
|
-
log(info: LogInfo) {
|
|
14
|
-
const { logger, level, args } = info
|
|
15
|
-
const method = ConsoleHandler.consoleMethods[level]
|
|
16
|
-
const levelColor = ConsoleHandler.levelColors[level]
|
|
17
|
-
const levelName = formatters.level(info)
|
|
18
|
-
const loggerColor = chalk[ConsoleHandler.getLoggerColor(logger)]
|
|
19
|
-
const prefix = [
|
|
20
|
-
chalk.white(`[${formatters.time(info)}]`),
|
|
21
|
-
levelColor(`[${levelName}]`),
|
|
22
|
-
...(logger ? [loggerColor(`[${logger}]`)] : []),
|
|
23
|
-
].join('')
|
|
24
|
-
method(prefix, ...args)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static readonly consoleMethods = {
|
|
28
|
-
[LogLevel.Debug]: console.debug.bind(console),
|
|
29
|
-
[LogLevel.Info]: console.info.bind(console),
|
|
30
|
-
[LogLevel.Warning]: console.warn.bind(console),
|
|
31
|
-
[LogLevel.Error]: console.error.bind(console),
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static readonly levelColors = {
|
|
35
|
-
[LogLevel.Debug]: chalk.whiteBright,
|
|
36
|
-
[LogLevel.Info]: chalk.white,
|
|
37
|
-
[LogLevel.Warning]: chalk.yellowBright,
|
|
38
|
-
[LogLevel.Error]: chalk.redBright,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// 可供 logger 选择的颜色
|
|
42
|
-
private static readonly loggerColors = [
|
|
43
|
-
'green',
|
|
44
|
-
'yellow',
|
|
45
|
-
'blue',
|
|
46
|
-
'magenta',
|
|
47
|
-
'cyan',
|
|
48
|
-
'greenBright',
|
|
49
|
-
'yellowBright',
|
|
50
|
-
'blueBright',
|
|
51
|
-
'magentaBright',
|
|
52
|
-
'cyanBright',
|
|
53
|
-
] as const
|
|
54
|
-
private static readonly loggerColorMap = new Map<string, (typeof this.loggerColors)[number]>()
|
|
55
|
-
static getLoggerColor(logger: string) {
|
|
56
|
-
if (!ConsoleHandler.loggerColorMap.has(logger)) {
|
|
57
|
-
const color =
|
|
58
|
-
ConsoleHandler.loggerColors[
|
|
59
|
-
ConsoleHandler.loggerColorMap.size % ConsoleHandler.loggerColors.length
|
|
60
|
-
]!
|
|
61
|
-
ConsoleHandler.loggerColorMap.set(logger, color)
|
|
62
|
-
}
|
|
63
|
-
return ConsoleHandler.loggerColorMap.get(logger)!
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* 写入文件日志
|
|
69
|
-
*/
|
|
70
|
-
export interface FileHandlerOptions {
|
|
71
|
-
dir: string // 日志存放目录 Logs directory
|
|
72
|
-
filePrefix: string // 日志文件名前缀 Prefix of the log file name
|
|
73
|
-
maxLength: number // 单条日志最长多少字符 Maximum length of a single log message
|
|
74
|
-
flushLength: number // 触发写入的缓存字符串数 Length of buffered strings that trigger a write operation
|
|
75
|
-
flushInterval: number // 缓存定时写入间隔(单位:ms),为 0 则所有日志立刻写入 Buffered strings write interval, 0 means all logs are written immediately.
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export class FileHandler extends LogHandler {
|
|
79
|
-
readonly options: FileHandlerOptions
|
|
80
|
-
|
|
81
|
-
constructor(options?: Partial<FileHandlerOptions>) {
|
|
82
|
-
super()
|
|
83
|
-
|
|
84
|
-
const dirname = path.dirname(fileURLToPath(new URL(import.meta.url)))
|
|
85
|
-
this.options = {
|
|
86
|
-
dir: path.resolve(dirname, 'logs'),
|
|
87
|
-
filePrefix: '',
|
|
88
|
-
maxLength: 10000,
|
|
89
|
-
flushLength: 100000,
|
|
90
|
-
flushInterval: 1000,
|
|
91
|
-
...(options ?? {}),
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
this.initLogDir()
|
|
95
|
-
|
|
96
|
-
// 进程退出前把尚未写入文件的日志强制写入
|
|
97
|
-
// 这里必须用同步的方式来写,不然会写入不进去(可能是因为异步的话是放到下一个事件循环,但进程在这个事件循环内就退出了)
|
|
98
|
-
process.on('exit', () => this.flush(true))
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Format log content
|
|
102
|
-
log(info: LogInfo) {
|
|
103
|
-
const { logger, args } = info
|
|
104
|
-
const prefix =
|
|
105
|
-
[
|
|
106
|
-
`[${formatters.datetime(info)}]`,
|
|
107
|
-
`[${formatters.level(info)}]`,
|
|
108
|
-
logger ? `[${logger}]` : '',
|
|
109
|
-
].join('') + ' '
|
|
110
|
-
|
|
111
|
-
const itemStrings: string[] = []
|
|
112
|
-
let totalLength = prefix.length
|
|
113
|
-
for (const item of args) {
|
|
114
|
-
const itemString = this.stringifyDataItem(item)
|
|
115
|
-
|
|
116
|
-
// 截断过长的日志内容 Truncate overly long log messages
|
|
117
|
-
if (totalLength + itemString.length < this.options.maxLength) {
|
|
118
|
-
itemStrings.push((totalLength === prefix.length ? '' : ' ') + itemString)
|
|
119
|
-
totalLength += itemString.length
|
|
120
|
-
} else {
|
|
121
|
-
itemStrings.push(
|
|
122
|
-
itemString.slice(0, this.options.maxLength - totalLength) + ' [too long, sliced]',
|
|
123
|
-
)
|
|
124
|
-
break
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
this.pushBuffer(prefix, ...itemStrings, '\n')
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
protected stringifyDataItem(item: unknown) {
|
|
132
|
-
// 去掉颜色控制字符
|
|
133
|
-
if (typeof item === 'string') item = item.replace(/\x1b\[\d+m/g, '')
|
|
134
|
-
|
|
135
|
-
// 利用 util.format() 获得和 console.log() 相同的输出(因为 console.log() 底层也是用的 util.format())
|
|
136
|
-
return util.format(item)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Handle buffer & flush
|
|
140
|
-
private buffer: string[] = []
|
|
141
|
-
private bufferSize = 0
|
|
142
|
-
private flushTimeoutId: NodeJS.Timeout | null = null
|
|
143
|
-
|
|
144
|
-
protected pushBuffer(...strings: string[]) {
|
|
145
|
-
this.buffer.push(...strings)
|
|
146
|
-
this.bufferSize = strings.reduce((sum, v) => sum + v.length, this.bufferSize)
|
|
147
|
-
if (this.options.flushInterval === 0 || this.bufferSize >= this.options.flushLength) {
|
|
148
|
-
this.flush()
|
|
149
|
-
} else if (!this.flushTimeoutId) {
|
|
150
|
-
this.flushTimeoutId = setTimeout(() => this.flush(), this.options.flushInterval)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
protected flush(sync?: boolean) {
|
|
155
|
-
if (this.flushTimeoutId) {
|
|
156
|
-
clearTimeout(this.flushTimeoutId)
|
|
157
|
-
this.flushTimeoutId = null
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (this.buffer.length) {
|
|
161
|
-
const content = this.buffer.join('')
|
|
162
|
-
this.buffer = []
|
|
163
|
-
this.bufferSize = 0
|
|
164
|
-
this.write(content, sync)
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// 文件系统交互 File system interaction
|
|
169
|
-
get filepath() {
|
|
170
|
-
const { dir, filePrefix } = this.options
|
|
171
|
-
return path.join(
|
|
172
|
-
dir,
|
|
173
|
-
`${filePrefix ? `${filePrefix}-` : ''}${dayjs().format('YYYY-MM-DD')}.log`,
|
|
174
|
-
)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
protected initLogDir() {
|
|
178
|
-
if (!fs.existsSync(this.options.dir)) fs.mkdirSync(this.options.dir)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
protected write(content: string, sync = false) {
|
|
182
|
-
if (sync) {
|
|
183
|
-
fs.appendFileSync(this.filepath, content)
|
|
184
|
-
} else {
|
|
185
|
-
fs.appendFile(this.filepath, content, error => {
|
|
186
|
-
if (error) console.error('[logger] write failed: ', error)
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 针对 Node.js 环境定制 logging
|
|
3
|
-
* 注意:使用此模块需要 chalk 依赖
|
|
4
|
-
*/
|
|
5
|
-
import { logger as defaultLogger, type Logger } from '../../logging/index.js'
|
|
6
|
-
import { ConsoleHandler } from './handlers.js'
|
|
7
|
-
|
|
8
|
-
export * from './handlers.js'
|
|
9
|
-
export * from '../../logging/index.js'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 预设的初始化行为
|
|
13
|
-
*/
|
|
14
|
-
export function initLogger(logger: Logger = defaultLogger) {
|
|
15
|
-
logger.addHandler(new ConsoleHandler())
|
|
16
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 通过 React Hook 把 emotion css 转换成 className
|
|
3
|
-
* (不再需要 <ClassName>)
|
|
4
|
-
*
|
|
5
|
-
* 使用前提:
|
|
6
|
-
* 1. 只支持浏览器渲染
|
|
7
|
-
* 2. 用 EmotionCacheProvider 包裹 App 根元素
|
|
8
|
-
*
|
|
9
|
-
* 来自:
|
|
10
|
-
* https://github.com/emotion-js/emotion/issues/1853#issuecomment-623349622
|
|
11
|
-
*/
|
|
12
|
-
import { type EmotionCache, withEmotionCache } from '@emotion/react'
|
|
13
|
-
import { type CSSInterpolation, serializeStyles } from '@emotion/serialize'
|
|
14
|
-
import { insertStyles } from '@emotion/utils'
|
|
15
|
-
import { createContext, useContext, useCallback } from 'react'
|
|
16
|
-
|
|
17
|
-
const CacheContext = createContext<EmotionCache | undefined>(undefined)
|
|
18
|
-
export const useEmotionCache = () => useContext(CacheContext)
|
|
19
|
-
|
|
20
|
-
export const EmotionCacheProvider = withEmotionCache(
|
|
21
|
-
({ children }: { children: React.ReactNode }, cache: EmotionCache) => {
|
|
22
|
-
return <CacheContext.Provider value={cache}>{children}</CacheContext.Provider>
|
|
23
|
-
},
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
export function useEmotionClassName(): (...args: CSSInterpolation[]) => string {
|
|
27
|
-
const cache = useEmotionCache()
|
|
28
|
-
return useCallback(
|
|
29
|
-
(...args) => {
|
|
30
|
-
if (!cache) {
|
|
31
|
-
if (process.env.NODE_ENV === 'production') {
|
|
32
|
-
return 'emotion-cache-missing'
|
|
33
|
-
}
|
|
34
|
-
throw new Error('No emotion cache found!')
|
|
35
|
-
}
|
|
36
|
-
const serialized = serializeStyles(args, cache.registered)
|
|
37
|
-
insertStyles(cache, serialized, false)
|
|
38
|
-
return cache.key + '-' + serialized.name
|
|
39
|
-
},
|
|
40
|
-
[cache],
|
|
41
|
-
)
|
|
42
|
-
}
|
package/src/env-react/hooks.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { useRef, useCallback } from 'react'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 生成一个 state 以及与其值同步的 ref
|
|
5
|
-
*/
|
|
6
|
-
export function useStateWithRef<T>(initialValue: T | (() => T)) {
|
|
7
|
-
const [state, setState] = useState(initialValue)
|
|
8
|
-
const ref = useRef(state)
|
|
9
|
-
|
|
10
|
-
const setStateWithRef: typeof setState = useCallback((value: T | ((prevState: T) => T)) => {
|
|
11
|
-
setState(prevState => {
|
|
12
|
-
const newValue =
|
|
13
|
-
typeof value === 'function' ? (value as (prevState: T) => T)(prevState) : value
|
|
14
|
-
ref.current = newValue
|
|
15
|
-
return newValue
|
|
16
|
-
})
|
|
17
|
-
}, [])
|
|
18
|
-
|
|
19
|
-
return [state, setStateWithRef, ref] as const
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 在 useEffect() 中执行异步操作。
|
|
24
|
-
* 因是异步运行,对清理机制的处理有所变化。
|
|
25
|
-
*
|
|
26
|
-
* 原本 useEffect() 中,通过返回一个函数来设置清理条件,在异步操作中不适用。
|
|
27
|
-
* 此外,同步内容是一定会在执行完只后才有可能触发清理的,但异步内容有可能在运行完之前,依赖就已经变化,触发了清理。
|
|
28
|
-
*
|
|
29
|
-
* 这里通过一个 context 对象来应对异步的情况。
|
|
30
|
-
* context.cancelled 值代表此次运行是否已被取消(清理),异步操作执行过程中如果发现此值变为 true,则可停止执行了。
|
|
31
|
-
* context.onCancelled(() => {}) 可注册一个回调,此次执行被清理时触发。多次调用仅保留最后一次的回调。
|
|
32
|
-
*
|
|
33
|
-
* 注意:需要配置 ESLint react-hooks/exhaustive-deps 规则以保证 deps 参与依赖检查
|
|
34
|
-
* 详见 https://react.dev/reference/eslint-plugin-react-hooks/lints/exhaustive-deps
|
|
35
|
-
*/
|
|
36
|
-
export function useAsyncEffect(
|
|
37
|
-
callback: (context: AsyncEffectContext) => Promise<void>,
|
|
38
|
-
deps: unknown[] = [],
|
|
39
|
-
) {
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
let onCancel: (() => void) | null = null
|
|
42
|
-
const context = {
|
|
43
|
-
cancelled: false,
|
|
44
|
-
onCancel(callback: () => void) {
|
|
45
|
-
onCancel = callback
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
void callback(context)
|
|
50
|
-
return () => {
|
|
51
|
-
context.cancelled = true
|
|
52
|
-
if (onCancel) onCancel()
|
|
53
|
-
}
|
|
54
|
-
}, deps) // eslint-disable-line ts-react-hooks/exhaustive-deps
|
|
55
|
-
}
|
|
56
|
-
export interface AsyncEffectContext {
|
|
57
|
-
cancelled: boolean
|
|
58
|
-
onCancel: (callback: () => void) => void
|
|
59
|
-
}
|
package/src/env-react/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './hooks.js'
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 把 React Hooks 注册成全局变量,就不用每次使用都手动引入了
|
|
3
|
-
* Hooks 列表见:https://react.dev/reference/react/hooks
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
useState as useStateValue,
|
|
8
|
-
useReducer as useReducerValue,
|
|
9
|
-
useContext as useContextValue,
|
|
10
|
-
useRef as useRefValue,
|
|
11
|
-
useImperativeHandle as useImperativeHandleValue,
|
|
12
|
-
useEffect as useEffectValue,
|
|
13
|
-
useMemo as useMemoValue,
|
|
14
|
-
useCallback as useCallbackValue,
|
|
15
|
-
useTransition as useTransitionValue,
|
|
16
|
-
useDeferredValue as useDeferredValueValue,
|
|
17
|
-
useDebugValue as useDebugValueValue,
|
|
18
|
-
useId as useIdValue,
|
|
19
|
-
useSyncExternalStore as useSyncExternalStoreValue,
|
|
20
|
-
useActionState as useActionStateValue,
|
|
21
|
-
} from 'react'
|
|
22
|
-
|
|
23
|
-
declare global {
|
|
24
|
-
var useState: typeof useStateValue
|
|
25
|
-
var useReducer: typeof useReducerValue
|
|
26
|
-
var useContext: typeof useContextValue
|
|
27
|
-
var useRef: typeof useRefValue
|
|
28
|
-
var useImperativeHandle: typeof useImperativeHandleValue
|
|
29
|
-
var useEffect: typeof useEffectValue
|
|
30
|
-
var useMemo: typeof useMemoValue
|
|
31
|
-
var useCallback: typeof useCallbackValue
|
|
32
|
-
var useTransition: typeof useTransitionValue
|
|
33
|
-
var useDeferredValue: typeof useDeferredValueValue
|
|
34
|
-
var useDebugValue: typeof useDebugValueValue
|
|
35
|
-
var useId: typeof useIdValue
|
|
36
|
-
var useSyncExternalStore: typeof useSyncExternalStoreValue
|
|
37
|
-
var useActionState: typeof useActionStateValue
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
globalThis.useState = useStateValue
|
|
41
|
-
globalThis.useReducer = useReducerValue
|
|
42
|
-
globalThis.useContext = useContextValue
|
|
43
|
-
globalThis.useRef = useRefValue
|
|
44
|
-
globalThis.useImperativeHandle = useImperativeHandleValue
|
|
45
|
-
globalThis.useEffect = useEffectValue
|
|
46
|
-
globalThis.useMemo = useMemoValue
|
|
47
|
-
globalThis.useCallback = useCallbackValue
|
|
48
|
-
globalThis.useTransition = useTransitionValue
|
|
49
|
-
globalThis.useDeferredValue = useDeferredValueValue
|
|
50
|
-
globalThis.useDebugValue = useDebugValueValue
|
|
51
|
-
globalThis.useId = useIdValue
|
|
52
|
-
globalThis.useSyncExternalStore = useSyncExternalStoreValue
|
|
53
|
-
globalThis.useActionState = useActionStateValue
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 把业务功能整理成各个 Controller,
|
|
3
|
-
* 并整合成一个 controllers 对象方便外部引用和 Controller 之间互相引用。
|
|
4
|
-
*
|
|
5
|
-
* 支持自定义 Controller 类,例如把 context 中的内容定义成属性。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/*
|
|
9
|
-
【使用范例】
|
|
10
|
-
|
|
11
|
-
// 定制 Context 和 Controller
|
|
12
|
-
interface MyContext {
|
|
13
|
-
prop1: number
|
|
14
|
-
prop2: string
|
|
15
|
-
}
|
|
16
|
-
class MyController<
|
|
17
|
-
AllControllers extends Record<string, AnyController<MyContext>>,
|
|
18
|
-
> extends Controller<MyContext, AllControllers> {
|
|
19
|
-
get prop1() {
|
|
20
|
-
return this.context.prop1
|
|
21
|
-
}
|
|
22
|
-
get prop2() {
|
|
23
|
-
return this.context.prop2
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 实现 Controller 内容
|
|
28
|
-
class C1 extends MyController<Controllers> {
|
|
29
|
-
someMethod() {
|
|
30
|
-
console.log(this.prop1)
|
|
31
|
-
console.log(this.controllers.c2.prop2)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
class C2 extends MyController<Controllers> {}
|
|
35
|
-
class C3 extends MyController<Controllers> {}
|
|
36
|
-
|
|
37
|
-
// 生成 controllers 类型和对象
|
|
38
|
-
export const controllerClasses = { c1: C1, c2: C2, c3: C3 }
|
|
39
|
-
export type Controllers = ControllersFrom<MyContext, typeof controllerClasses>
|
|
40
|
-
initializeControllers({ c1: C1, c2: C2, c3: C3 }, { prop1: 1, prop2: 2 })
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
export type AnyObject = Record<string, unknown>
|
|
44
|
-
export type AnyController<Context> = Controller<Context, any> // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
45
|
-
type AnyControllerClass<Context> = typeof Controller<Context, any> // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
46
|
-
type ControllerClassesFrom<Context, T extends AnyObject> = {
|
|
47
|
-
[K in keyof T]: T[K] extends AnyControllerClass<Context> ? T[K] : never
|
|
48
|
-
}
|
|
49
|
-
export type ControllersFrom<Context, T extends AnyObject> = {
|
|
50
|
-
[K in keyof T]: T[K] extends AnyControllerClass<Context> ? InstanceType<T[K]> : never
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Controller 基类
|
|
55
|
-
*/
|
|
56
|
-
export class Controller<Context, AllControllers extends Record<string, AnyController<Context>>> {
|
|
57
|
-
constructor(
|
|
58
|
-
/** 调用其他 controllers */
|
|
59
|
-
protected readonly controllers: AllControllers,
|
|
60
|
-
|
|
61
|
-
protected readonly context: Context,
|
|
62
|
-
|
|
63
|
-
protected readonly name: string = this.constructor.name,
|
|
64
|
-
) {}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* 传入 Controller 类列表,返回 controller 实例集合。
|
|
69
|
-
* 为优化性能,每个 controller 只有在被使用到时才会实例化。
|
|
70
|
-
*/
|
|
71
|
-
export function initializeControllers<Context, T extends AnyObject>(
|
|
72
|
-
controllerClasses: T,
|
|
73
|
-
context: Context,
|
|
74
|
-
) {
|
|
75
|
-
type Classes = ControllerClassesFrom<Context, T>
|
|
76
|
-
type Controllers = ControllersFrom<Context, T>
|
|
77
|
-
const proxy = new Proxy({} as Controllers, {
|
|
78
|
-
get(controllers, prop) {
|
|
79
|
-
if (typeof prop !== 'string') return
|
|
80
|
-
if (prop in controllers) return controllers[prop]
|
|
81
|
-
if (prop in controllerClasses) {
|
|
82
|
-
const Class = controllerClasses[prop]! as typeof Controller<Context, Controllers>
|
|
83
|
-
controllers[prop as keyof Classes] = new Class(
|
|
84
|
-
proxy,
|
|
85
|
-
context,
|
|
86
|
-
prop,
|
|
87
|
-
) as Controllers[keyof Classes]
|
|
88
|
-
return controllers[prop]
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
})
|
|
92
|
-
return proxy
|
|
93
|
-
}
|