@cmtlyt/lingshu-toolkit 0.1.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/README.md +11 -0
- package/dist/607.js +131 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/use-controllable-value/index.d.ts +14 -0
- package/dist/react/use-controllable-value/index.js +33 -0
- package/dist/react/use-controllable-value/index.test.d.ts +1 -0
- package/dist/react/use-counter/index.d.ts +12 -0
- package/dist/react/use-counter/index.js +49 -0
- package/dist/react/use-counter/index.test.d.ts +1 -0
- package/dist/react/use-valid-data/index.d.ts +6 -0
- package/dist/react/use-valid-data/index.js +11 -0
- package/dist/react/use-valid-data/index.test.d.ts +1 -0
- package/dist/react.js +86 -0
- package/dist/shared/data-handler/index.d.ts +6 -0
- package/dist/shared/data-handler/index.js +77 -0
- package/dist/shared/data-handler/index.test.d.ts +1 -0
- package/dist/shared/data-handler/tools.d.ts +18 -0
- package/dist/shared/data-handler/types.d.ts +26 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/logger/index.d.ts +1 -0
- package/dist/shared/logger/index.js +9 -0
- package/dist/shared/throw-error/index.d.ts +2 -0
- package/dist/shared/throw-error/index.js +7 -0
- package/dist/shared/throw-error/index.test.d.ts +1 -0
- package/dist/shared/types/index.d.ts +2 -0
- package/dist/shared/types/index.js +0 -0
- package/dist/shared.js +1 -0
- package/package.json +112 -0
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @cmtlyt/lingshu-toolkit
|
|
2
|
+
|
|
3
|
+
一个支持 shadcn 的工具库
|
|
4
|
+
|
|
5
|
+
## 为什么要支持 shadcn?
|
|
6
|
+
|
|
7
|
+
如果是纯 shadcn 的话, 就会失去长期维护能力, 作者对包的后续修改也很难进行更新, 所以对于这个痛点, 我希望这个包能同时支持 shadcn 和 npm package
|
|
8
|
+
|
|
9
|
+
然后 shadcn 可以提供很好的定制化, 而且 npm 包有 bug 的话也可以通过 shadcn 拉取并修改, 修改完成后还能通过 pr 的方式重新提交回 npm 包, 这样就可以获取 npm 包的长期维护能力了
|
|
10
|
+
|
|
11
|
+
shadcn 的安装方式可以通过[官方文档](https://cmtlyt.github.io/lingshu-toolkit/)查看, 文档后续会慢慢完善的
|
package/dist/607.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
function throwError(message, ErrorClass = Error) {
|
|
2
|
+
throw new ErrorClass(`[@cmtlyt/lingshu-toolkit]: ${message}`);
|
|
3
|
+
}
|
|
4
|
+
function throwType(message) {
|
|
5
|
+
throwError(message, TypeError);
|
|
6
|
+
}
|
|
7
|
+
const logger = new Proxy(console, {
|
|
8
|
+
get (target, prop, receiver) {
|
|
9
|
+
const oldLog = Reflect.get(target, prop, receiver).bind(console);
|
|
10
|
+
return (...args)=>{
|
|
11
|
+
oldLog('[@cmtlyt/lingshu-toolkit]:', ...args);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
function getType(_v) {
|
|
16
|
+
return Object.prototype.toString.call(_v).slice(8, -1).toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
function typeHandler(type, verifyFn) {
|
|
19
|
+
return (fullback)=>(_v, actions)=>{
|
|
20
|
+
if (verifyFn ? verifyFn(_v) : getType(_v) === type) return true;
|
|
21
|
+
if (null == fullback) return false;
|
|
22
|
+
let fullbackValue = fullback;
|
|
23
|
+
if ('function' == typeof fullback) fullbackValue = fullback(_v);
|
|
24
|
+
actions.transform(fullbackValue);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const $t = {
|
|
28
|
+
notNullable: typeHandler('notNullable', (_v)=>null != _v),
|
|
29
|
+
string: typeHandler('string'),
|
|
30
|
+
validString: typeHandler('validString', (_v)=>'string' == typeof _v && _v.length > 0),
|
|
31
|
+
number: typeHandler('number'),
|
|
32
|
+
validNumber: typeHandler('validNumber', (_v)=>'number' == typeof _v && !Number.isNaN(_v)),
|
|
33
|
+
boolean: typeHandler('boolean'),
|
|
34
|
+
object: typeHandler('object'),
|
|
35
|
+
array: typeHandler('array'),
|
|
36
|
+
function: typeHandler('function'),
|
|
37
|
+
symbol: typeHandler('symbol')
|
|
38
|
+
};
|
|
39
|
+
function defineTransform(dataInfo) {
|
|
40
|
+
const verifyInfo = {};
|
|
41
|
+
const keys = Reflect.ownKeys(dataInfo);
|
|
42
|
+
for(let i = 0, key = keys[i], item = dataInfo[key]; i < keys.length; key = keys[++i], item = dataInfo[key]){
|
|
43
|
+
if ('function' == typeof item) {
|
|
44
|
+
verifyInfo[key] = item;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const handler = $t[item];
|
|
48
|
+
if (!handler) {
|
|
49
|
+
logger.warn(`${item} is not a valid type`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
verifyInfo[key] = handler();
|
|
53
|
+
}
|
|
54
|
+
return verifyInfo;
|
|
55
|
+
}
|
|
56
|
+
const $dt = defineTransform;
|
|
57
|
+
function createActions() {
|
|
58
|
+
const ctx = {
|
|
59
|
+
errors: [],
|
|
60
|
+
transforms: [],
|
|
61
|
+
handledErrorKeys: new Set()
|
|
62
|
+
};
|
|
63
|
+
const handler = {
|
|
64
|
+
addError (key, msg = `${String(key)} is not valid`) {
|
|
65
|
+
if (ctx.handledErrorKeys.has(key)) return;
|
|
66
|
+
ctx.handledErrorKeys.add(key);
|
|
67
|
+
ctx.errors.push(msg);
|
|
68
|
+
},
|
|
69
|
+
addTransform (key, value) {
|
|
70
|
+
ctx.transforms.push([
|
|
71
|
+
key,
|
|
72
|
+
value
|
|
73
|
+
]);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return [
|
|
77
|
+
ctx,
|
|
78
|
+
handler,
|
|
79
|
+
(key)=>({
|
|
80
|
+
assert: (flag, msg = `${String(key)} is not valid`)=>{
|
|
81
|
+
if (!flag) handler.addError(key, msg);
|
|
82
|
+
return flag;
|
|
83
|
+
},
|
|
84
|
+
transform: (value)=>{
|
|
85
|
+
if (!ctx.handledErrorKeys.has(key)) handler.addTransform(key, value);
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
function transformApply(data, transforms) {
|
|
92
|
+
if (!transforms.length) return;
|
|
93
|
+
for(let i = 0, [key, value] = transforms[i]; i < transforms.length; [key, value] = transforms[++i] || [])data[key] = value;
|
|
94
|
+
}
|
|
95
|
+
function handleProcess(data, keys, handleFn, getActions, actionHandlers) {
|
|
96
|
+
for(let i = 0, key = keys[i]; i < keys.length; key = keys[++i]){
|
|
97
|
+
const flag = handleFn(data[key], key, getActions(key), data);
|
|
98
|
+
if (false === flag) actionHandlers.addError(key);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function errorProcess(errors, errorHandler, strict) {
|
|
102
|
+
if (!errors.length) return;
|
|
103
|
+
if (errorHandler) errorHandler(errors);
|
|
104
|
+
else if (strict) throwType(errors.join('\n'));
|
|
105
|
+
}
|
|
106
|
+
function filterData(data, ctx, defaultValue = {}) {
|
|
107
|
+
ctx.handledErrorKeys.forEach((key)=>{
|
|
108
|
+
data[key] = defaultValue[key];
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function dataHandler(data, handler, options) {
|
|
112
|
+
if (!handler) throwType('handler is required');
|
|
113
|
+
const { strict = false, errorHandler, defaultValue } = options || {};
|
|
114
|
+
const handlerIsFunction = 'function' == typeof handler;
|
|
115
|
+
const handleFn = handlerIsFunction ? handler : (value, key, ...args)=>handler[key](value, ...args);
|
|
116
|
+
const tempData = {
|
|
117
|
+
...defaultValue,
|
|
118
|
+
...data
|
|
119
|
+
};
|
|
120
|
+
const keys = handlerIsFunction ? Reflect.ownKeys(data) : Reflect.ownKeys(handler);
|
|
121
|
+
const [ctx, actionHandler, getActions] = createActions();
|
|
122
|
+
handleProcess(tempData, keys, handleFn, getActions, actionHandler);
|
|
123
|
+
errorProcess(ctx.errors, errorHandler, strict);
|
|
124
|
+
transformApply(tempData, ctx.transforms);
|
|
125
|
+
filterData(tempData, ctx, defaultValue);
|
|
126
|
+
return {
|
|
127
|
+
result: tempData,
|
|
128
|
+
errors: ctx.errors
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export { $dt, $t, dataHandler, defineTransform, logger, throwError, throwType };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NotUnion } from '../../shared/types';
|
|
2
|
+
interface UseControllableValueOptions<Ks extends PropertyKey = PropertyKey, P extends Ks | (string & {}) = 'value'> {
|
|
3
|
+
defaultValue: any;
|
|
4
|
+
defaultValuePropName: Ks;
|
|
5
|
+
valuePropName: P;
|
|
6
|
+
trigger: Ks;
|
|
7
|
+
}
|
|
8
|
+
export type PublicUseControllableValueOptions<Ks extends PropertyKey = PropertyKey, P extends Ks | (string & {}) = 'value'> = Partial<UseControllableValueOptions<Ks, P>>;
|
|
9
|
+
type ValueType<T extends Record<PropertyKey, any>, P> = NotUnion<P> extends true ? P extends keyof T ? T[P] : T['value'] : T['value'];
|
|
10
|
+
/**
|
|
11
|
+
* 受控组件 value 逻辑切换, 如果传递了 value 则走受控逻辑, 否则走非受控逻辑
|
|
12
|
+
*/
|
|
13
|
+
export declare function useControllableValue<T extends Record<PropertyKey, any>, P extends keyof T | (string & {}) = PropertyKey>(props?: T, options?: PublicUseControllableValueOptions<keyof T, P>): [ValueType<T, P>, (value: ValueType<T, P>, ...args: any[]) => void];
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useEffect, useEffectEvent, useRef, useState } from "react";
|
|
2
|
+
import { $dt, $t, useValidData } from "../use-valid-data/index.js";
|
|
3
|
+
const validInfo = $dt({
|
|
4
|
+
defaultValuePropName: $t.validString('defaultValue'),
|
|
5
|
+
valuePropName: $t.validString('value'),
|
|
6
|
+
trigger: $t.validString('onChange')
|
|
7
|
+
});
|
|
8
|
+
function useControllableValue(props = {}, options = {}) {
|
|
9
|
+
const { result: validOptions } = useValidData(options, validInfo);
|
|
10
|
+
const { defaultValue: _defaultValue, trigger, valuePropName, defaultValuePropName } = validOptions;
|
|
11
|
+
const { [valuePropName]: propValue, [defaultValuePropName]: defaultValue = _defaultValue, [trigger]: emitChange } = props;
|
|
12
|
+
const hasValueRef = useRef(Boolean(Reflect.getOwnPropertyDescriptor(props, valuePropName)));
|
|
13
|
+
const isFirstRenderRef = useRef(true);
|
|
14
|
+
const [ctrlValue, setCtrlValue] = useState(hasValueRef.current ? propValue : defaultValue);
|
|
15
|
+
const setValue = useEffectEvent((value, ...args)=>{
|
|
16
|
+
if ('function' == typeof emitChange) emitChange(value, ...args);
|
|
17
|
+
if (!hasValueRef.current) setCtrlValue(value);
|
|
18
|
+
});
|
|
19
|
+
useEffect(()=>{
|
|
20
|
+
if (isFirstRenderRef.current || !hasValueRef.current) {
|
|
21
|
+
isFirstRenderRef.current = false;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
setCtrlValue(propValue);
|
|
25
|
+
}, [
|
|
26
|
+
propValue
|
|
27
|
+
]);
|
|
28
|
+
return [
|
|
29
|
+
ctrlValue,
|
|
30
|
+
setValue
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
export { useControllableValue };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface UseCounterOptions {
|
|
2
|
+
min: number;
|
|
3
|
+
max: number;
|
|
4
|
+
step: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function useCounter(initialValue?: number, options?: Partial<UseCounterOptions>): readonly [number, {
|
|
7
|
+
increment: (delta?: number) => void;
|
|
8
|
+
decrement: (delta?: number) => void;
|
|
9
|
+
reset: () => void;
|
|
10
|
+
set: (value: number | ((prev: number) => number)) => void;
|
|
11
|
+
}];
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { logger } from "../../shared/logger/index.js";
|
|
2
|
+
import { useEffectEvent, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { $dt, $t, useValidData } from "../use-valid-data/index.js";
|
|
4
|
+
function getRealValue(value, options) {
|
|
5
|
+
const { min, max } = options;
|
|
6
|
+
return Math.min(Math.max(value, min), max);
|
|
7
|
+
}
|
|
8
|
+
function nanTransform(_dv) {
|
|
9
|
+
return (_v)=>{
|
|
10
|
+
if (void 0 === _v) return _dv;
|
|
11
|
+
logger.warn('useCounter', 'value is not a number', _v, 'useCounter will use default value');
|
|
12
|
+
const numV = Number(_v);
|
|
13
|
+
if (Number.isNaN(numV)) return _dv;
|
|
14
|
+
return numV;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const validInfo = $dt({
|
|
18
|
+
min: $t.validNumber(nanTransform(-1 / 0)),
|
|
19
|
+
max: $t.validNumber(nanTransform(1 / 0)),
|
|
20
|
+
step: $t.validNumber(nanTransform(1))
|
|
21
|
+
});
|
|
22
|
+
function useCounter(initialValue = 0, options = {}) {
|
|
23
|
+
const { result: validOptions } = useValidData(options, validInfo);
|
|
24
|
+
const { step } = validOptions;
|
|
25
|
+
const initialValueRef = useRef(getRealValue(Number(initialValue), validOptions));
|
|
26
|
+
const [current, setCurrent] = useState(getRealValue(Number(initialValue), validOptions));
|
|
27
|
+
const setValue = useEffectEvent((value)=>{
|
|
28
|
+
setCurrent(()=>getRealValue('function' == typeof value ? value(current) : value, validOptions));
|
|
29
|
+
});
|
|
30
|
+
const actions = useMemo(()=>{
|
|
31
|
+
const _step = Math.abs(step);
|
|
32
|
+
const increment = (delta = _step)=>setValue((prev)=>prev + delta);
|
|
33
|
+
const decrement = (delta = _step)=>setValue((prev)=>prev - delta);
|
|
34
|
+
const reset = ()=>setCurrent(initialValueRef.current);
|
|
35
|
+
return {
|
|
36
|
+
increment,
|
|
37
|
+
decrement,
|
|
38
|
+
reset,
|
|
39
|
+
set: setValue
|
|
40
|
+
};
|
|
41
|
+
}, [
|
|
42
|
+
step
|
|
43
|
+
]);
|
|
44
|
+
return [
|
|
45
|
+
current,
|
|
46
|
+
actions
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
export { useCounter };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DataHandlerOptions, Handler } from '../../shared/data-handler/types';
|
|
2
|
+
export * from '../../shared/data-handler/tools';
|
|
3
|
+
export declare function useValidData<T extends Record<PropertyKey, any>>(data: T, verifyInfo: Handler<T>, options?: DataHandlerOptions<T>): {
|
|
4
|
+
result: T | (T & undefined);
|
|
5
|
+
errors: string[];
|
|
6
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { dataHandler } from "../../shared/index.js";
|
|
2
|
+
import { useMemo, useRef } from "react";
|
|
3
|
+
export * from "../../shared/data-handler/tools.js";
|
|
4
|
+
function useValidData(data, verifyInfo, options) {
|
|
5
|
+
const verifyInfoRef = useRef(verifyInfo);
|
|
6
|
+
const optionsRef = useRef(options);
|
|
7
|
+
return useMemo(()=>dataHandler(data, verifyInfoRef.current, optionsRef.current), [
|
|
8
|
+
data
|
|
9
|
+
]);
|
|
10
|
+
}
|
|
11
|
+
export { useValidData };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useEffect, useEffectEvent, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { dataHandler, logger, $dt, $t } from "./607.js";
|
|
3
|
+
function useValidData(data, verifyInfo, options) {
|
|
4
|
+
const verifyInfoRef = useRef(verifyInfo);
|
|
5
|
+
const optionsRef = useRef(options);
|
|
6
|
+
return useMemo(()=>dataHandler(data, verifyInfoRef.current, optionsRef.current), [
|
|
7
|
+
data
|
|
8
|
+
]);
|
|
9
|
+
}
|
|
10
|
+
const validInfo = $dt({
|
|
11
|
+
defaultValuePropName: $t.validString('defaultValue'),
|
|
12
|
+
valuePropName: $t.validString('value'),
|
|
13
|
+
trigger: $t.validString('onChange')
|
|
14
|
+
});
|
|
15
|
+
function useControllableValue(props = {}, options = {}) {
|
|
16
|
+
const { result: validOptions } = useValidData(options, validInfo);
|
|
17
|
+
const { defaultValue: _defaultValue, trigger, valuePropName, defaultValuePropName } = validOptions;
|
|
18
|
+
const { [valuePropName]: propValue, [defaultValuePropName]: defaultValue = _defaultValue, [trigger]: emitChange } = props;
|
|
19
|
+
const hasValueRef = useRef(Boolean(Reflect.getOwnPropertyDescriptor(props, valuePropName)));
|
|
20
|
+
const isFirstRenderRef = useRef(true);
|
|
21
|
+
const [ctrlValue, setCtrlValue] = useState(hasValueRef.current ? propValue : defaultValue);
|
|
22
|
+
const setValue = useEffectEvent((value, ...args)=>{
|
|
23
|
+
if ('function' == typeof emitChange) emitChange(value, ...args);
|
|
24
|
+
if (!hasValueRef.current) setCtrlValue(value);
|
|
25
|
+
});
|
|
26
|
+
useEffect(()=>{
|
|
27
|
+
if (isFirstRenderRef.current || !hasValueRef.current) {
|
|
28
|
+
isFirstRenderRef.current = false;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
setCtrlValue(propValue);
|
|
32
|
+
}, [
|
|
33
|
+
propValue
|
|
34
|
+
]);
|
|
35
|
+
return [
|
|
36
|
+
ctrlValue,
|
|
37
|
+
setValue
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
function getRealValue(value, options) {
|
|
41
|
+
const { min, max } = options;
|
|
42
|
+
return Math.min(Math.max(value, min), max);
|
|
43
|
+
}
|
|
44
|
+
function nanTransform(_dv) {
|
|
45
|
+
return (_v)=>{
|
|
46
|
+
if (void 0 === _v) return _dv;
|
|
47
|
+
logger.warn('useCounter', 'value is not a number', _v, 'useCounter will use default value');
|
|
48
|
+
const numV = Number(_v);
|
|
49
|
+
if (Number.isNaN(numV)) return _dv;
|
|
50
|
+
return numV;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const use_counter_validInfo = $dt({
|
|
54
|
+
min: $t.validNumber(nanTransform(-1 / 0)),
|
|
55
|
+
max: $t.validNumber(nanTransform(1 / 0)),
|
|
56
|
+
step: $t.validNumber(nanTransform(1))
|
|
57
|
+
});
|
|
58
|
+
function useCounter(initialValue = 0, options = {}) {
|
|
59
|
+
const { result: validOptions } = useValidData(options, use_counter_validInfo);
|
|
60
|
+
const { step } = validOptions;
|
|
61
|
+
const initialValueRef = useRef(getRealValue(Number(initialValue), validOptions));
|
|
62
|
+
const [current, setCurrent] = useState(getRealValue(Number(initialValue), validOptions));
|
|
63
|
+
const setValue = useEffectEvent((value)=>{
|
|
64
|
+
setCurrent(()=>getRealValue('function' == typeof value ? value(current) : value, validOptions));
|
|
65
|
+
});
|
|
66
|
+
const actions = useMemo(()=>{
|
|
67
|
+
const _step = Math.abs(step);
|
|
68
|
+
const increment = (delta = _step)=>setValue((prev)=>prev + delta);
|
|
69
|
+
const decrement = (delta = _step)=>setValue((prev)=>prev - delta);
|
|
70
|
+
const reset = ()=>setCurrent(initialValueRef.current);
|
|
71
|
+
return {
|
|
72
|
+
increment,
|
|
73
|
+
decrement,
|
|
74
|
+
reset,
|
|
75
|
+
set: setValue
|
|
76
|
+
};
|
|
77
|
+
}, [
|
|
78
|
+
step
|
|
79
|
+
]);
|
|
80
|
+
return [
|
|
81
|
+
current,
|
|
82
|
+
actions
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
export { $dt, $t, defineTransform } from "./607.js";
|
|
86
|
+
export { useControllableValue, useCounter, useValidData };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DataHandlerOptions, Handler } from './types';
|
|
2
|
+
export declare function dataHandler<M extends Record<PropertyKey, any>, O extends DataHandlerOptions<M> = DataHandlerOptions<M>>(data: M & Partial<O['defaultValue']>, handler: Handler<M>, options?: O): {
|
|
3
|
+
result: M & O['defaultValue'];
|
|
4
|
+
errors: string[];
|
|
5
|
+
};
|
|
6
|
+
export * from './tools';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { throwType } from "../throw-error/index.js";
|
|
2
|
+
export * from "./tools.js";
|
|
3
|
+
function createActions() {
|
|
4
|
+
const ctx = {
|
|
5
|
+
errors: [],
|
|
6
|
+
transforms: [],
|
|
7
|
+
handledErrorKeys: new Set()
|
|
8
|
+
};
|
|
9
|
+
const handler = {
|
|
10
|
+
addError (key, msg = `${String(key)} is not valid`) {
|
|
11
|
+
if (ctx.handledErrorKeys.has(key)) return;
|
|
12
|
+
ctx.handledErrorKeys.add(key);
|
|
13
|
+
ctx.errors.push(msg);
|
|
14
|
+
},
|
|
15
|
+
addTransform (key, value) {
|
|
16
|
+
ctx.transforms.push([
|
|
17
|
+
key,
|
|
18
|
+
value
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return [
|
|
23
|
+
ctx,
|
|
24
|
+
handler,
|
|
25
|
+
(key)=>({
|
|
26
|
+
assert: (flag, msg = `${String(key)} is not valid`)=>{
|
|
27
|
+
if (!flag) handler.addError(key, msg);
|
|
28
|
+
return flag;
|
|
29
|
+
},
|
|
30
|
+
transform: (value)=>{
|
|
31
|
+
if (!ctx.handledErrorKeys.has(key)) handler.addTransform(key, value);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
function transformApply(data, transforms) {
|
|
38
|
+
if (!transforms.length) return;
|
|
39
|
+
for(let i = 0, [key, value] = transforms[i]; i < transforms.length; [key, value] = transforms[++i] || [])data[key] = value;
|
|
40
|
+
}
|
|
41
|
+
function handleProcess(data, keys, handleFn, getActions, actionHandlers) {
|
|
42
|
+
for(let i = 0, key = keys[i]; i < keys.length; key = keys[++i]){
|
|
43
|
+
const flag = handleFn(data[key], key, getActions(key), data);
|
|
44
|
+
if (false === flag) actionHandlers.addError(key);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function errorProcess(errors, errorHandler, strict) {
|
|
48
|
+
if (!errors.length) return;
|
|
49
|
+
if (errorHandler) errorHandler(errors);
|
|
50
|
+
else if (strict) throwType(errors.join('\n'));
|
|
51
|
+
}
|
|
52
|
+
function filterData(data, ctx, defaultValue = {}) {
|
|
53
|
+
ctx.handledErrorKeys.forEach((key)=>{
|
|
54
|
+
data[key] = defaultValue[key];
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function dataHandler(data, handler, options) {
|
|
58
|
+
if (!handler) throwType('handler is required');
|
|
59
|
+
const { strict = false, errorHandler, defaultValue } = options || {};
|
|
60
|
+
const handlerIsFunction = 'function' == typeof handler;
|
|
61
|
+
const handleFn = handlerIsFunction ? handler : (value, key, ...args)=>handler[key](value, ...args);
|
|
62
|
+
const tempData = {
|
|
63
|
+
...defaultValue,
|
|
64
|
+
...data
|
|
65
|
+
};
|
|
66
|
+
const keys = handlerIsFunction ? Reflect.ownKeys(data) : Reflect.ownKeys(handler);
|
|
67
|
+
const [ctx, actionHandler, getActions] = createActions();
|
|
68
|
+
handleProcess(tempData, keys, handleFn, getActions, actionHandler);
|
|
69
|
+
errorProcess(ctx.errors, errorHandler, strict);
|
|
70
|
+
transformApply(tempData, ctx.transforms);
|
|
71
|
+
filterData(tempData, ctx, defaultValue);
|
|
72
|
+
return {
|
|
73
|
+
result: tempData,
|
|
74
|
+
errors: ctx.errors
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export { dataHandler };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Handler } from './types';
|
|
2
|
+
type TypeHandler = NonNullable<Exclude<Handler<any>, (...args: any[]) => any>[string]>;
|
|
3
|
+
type Fullback = ((_v: any) => any) | (any & {});
|
|
4
|
+
export declare const $t: {
|
|
5
|
+
notNullable: (fullback?: Fullback) => TypeHandler;
|
|
6
|
+
string: (fullback?: Fullback) => TypeHandler;
|
|
7
|
+
validString: (fullback?: Fullback) => TypeHandler;
|
|
8
|
+
number: (fullback?: Fullback) => TypeHandler;
|
|
9
|
+
validNumber: (fullback?: Fullback) => TypeHandler;
|
|
10
|
+
boolean: (fullback?: Fullback) => TypeHandler;
|
|
11
|
+
object: (fullback?: Fullback) => TypeHandler;
|
|
12
|
+
array: (fullback?: Fullback) => TypeHandler;
|
|
13
|
+
function: (fullback?: Fullback) => TypeHandler;
|
|
14
|
+
symbol: (fullback?: Fullback) => TypeHandler;
|
|
15
|
+
};
|
|
16
|
+
export declare function defineTransform<T extends Record<PropertyKey, any>>(dataInfo: Partial<Record<keyof T, keyof typeof $t | TypeHandler>>): Record<PropertyKey, (value: any, action: import("./types").Actions, option: any) => false | (any & {})>;
|
|
17
|
+
export declare const $dt: typeof defineTransform;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface Actions {
|
|
2
|
+
assert: <T extends boolean>(flag: T, msg?: string) => T;
|
|
3
|
+
/**
|
|
4
|
+
* 转换结果中的数值类型
|
|
5
|
+
*
|
|
6
|
+
* @warning 如果被 assert 处理为 false, 则不会应用转换
|
|
7
|
+
*/
|
|
8
|
+
transform: <T>(value: T) => T;
|
|
9
|
+
}
|
|
10
|
+
export type Handler<M extends Record<PropertyKey, any>> = Partial<{
|
|
11
|
+
[K in keyof M]: (value: M[K], action: Actions, option: M) => false | (any & {});
|
|
12
|
+
}> | (<K extends keyof M>(value: M[K], key: K, action: Actions, option: M) => false | (any & {}));
|
|
13
|
+
export interface ActionContext {
|
|
14
|
+
errors: string[];
|
|
15
|
+
transforms: [PropertyKey, any][];
|
|
16
|
+
handledErrorKeys: Set<PropertyKey>;
|
|
17
|
+
}
|
|
18
|
+
export interface ActionHandlers {
|
|
19
|
+
addError(key: PropertyKey, msg?: string): void;
|
|
20
|
+
addTransform(key: PropertyKey, value: any): void;
|
|
21
|
+
}
|
|
22
|
+
export interface DataHandlerOptions<M extends Record<PropertyKey, any>> {
|
|
23
|
+
strict?: boolean;
|
|
24
|
+
errorHandler?: (error: ActionContext['errors']) => void;
|
|
25
|
+
defaultValue?: M;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const logger: Console;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { $dt, $t, dataHandler, defineTransform, throwError, throwType } from "./607.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cmtlyt/lingshu-toolkit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/shared.js",
|
|
6
|
+
"module": "./dist/shared.js",
|
|
7
|
+
"types": "./dist/shared/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/shared/index.d.ts",
|
|
11
|
+
"import": "./dist/shared.js"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"types": "./dist/react/index.d.ts",
|
|
15
|
+
"import": "./dist/react.js"
|
|
16
|
+
},
|
|
17
|
+
"./react/*": {
|
|
18
|
+
"import": "./dist/react/*"
|
|
19
|
+
},
|
|
20
|
+
"./shared": {
|
|
21
|
+
"types": "./dist/shared/index.d.ts",
|
|
22
|
+
"import": "./dist/shared.js"
|
|
23
|
+
},
|
|
24
|
+
"./shared/*": {
|
|
25
|
+
"import": "./dist/shared/*"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "cmtlyt",
|
|
30
|
+
"email": "cmtlyt@163.com"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@biomejs/biome": "2.3.8",
|
|
34
|
+
"@cmtlyt/unplugin-shadcn-registry-generate": "^0.1.3",
|
|
35
|
+
"@commitlint/cli": "^20.3.1",
|
|
36
|
+
"@commitlint/config-conventional": "^20.3.1",
|
|
37
|
+
"@rslib/core": "^0.19.3",
|
|
38
|
+
"@types/node": "^24.10.9",
|
|
39
|
+
"@types/react": "^19.2.9",
|
|
40
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
41
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
42
|
+
"@vitest/browser-playwright": "4.0.18",
|
|
43
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
44
|
+
"@vitest/ui": "4.0.18",
|
|
45
|
+
"esno": "^4.8.0",
|
|
46
|
+
"husky": "^9.1.7",
|
|
47
|
+
"jsdom": "^27.4.0",
|
|
48
|
+
"lint-staged": "^16.2.7",
|
|
49
|
+
"react": "^19.2.4",
|
|
50
|
+
"react-dom": "^19.2.4",
|
|
51
|
+
"rspress": "^1.47.1",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"vite-tsconfig-paths": "^6.0.4",
|
|
54
|
+
"vitest": "4.0.18",
|
|
55
|
+
"vitest-browser-react": "2.0.2",
|
|
56
|
+
"vitest-browser-vue": "2.0.2",
|
|
57
|
+
"vue": "^3.5.27"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"react": "^19.2.3",
|
|
61
|
+
"vue": "^3.5.27"
|
|
62
|
+
},
|
|
63
|
+
"devEngines": {
|
|
64
|
+
"runtime": {
|
|
65
|
+
"name": "node",
|
|
66
|
+
"version": ">=18.0.0"
|
|
67
|
+
},
|
|
68
|
+
"packageManager": {
|
|
69
|
+
"name": "pnpm"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public",
|
|
74
|
+
"registry": "https://registry.npmjs.org/"
|
|
75
|
+
},
|
|
76
|
+
"homepage": "https://cmtlyt.github.io/lingshu-toolkit/",
|
|
77
|
+
"repository": {
|
|
78
|
+
"type": "git",
|
|
79
|
+
"url": "git+https://github.com/cmtlyt/lingshu-toolkit.git"
|
|
80
|
+
},
|
|
81
|
+
"bugs": {
|
|
82
|
+
"url": "https://github.com/cmtlyt/lingshu-toolkit/issues"
|
|
83
|
+
},
|
|
84
|
+
"license": "MIT",
|
|
85
|
+
"files": [
|
|
86
|
+
"dist"
|
|
87
|
+
],
|
|
88
|
+
"keywords": [
|
|
89
|
+
"cmtlyt",
|
|
90
|
+
"lingshu-toolkit",
|
|
91
|
+
"hook",
|
|
92
|
+
"hooks",
|
|
93
|
+
"shadcn",
|
|
94
|
+
"shadcn-hook",
|
|
95
|
+
"vue",
|
|
96
|
+
"react",
|
|
97
|
+
"vue-hook",
|
|
98
|
+
"react-hook"
|
|
99
|
+
],
|
|
100
|
+
"scripts": {
|
|
101
|
+
"build": "rslib build",
|
|
102
|
+
"build:docs": "rspress build",
|
|
103
|
+
"check": "biome check --write",
|
|
104
|
+
"dev:docs": "rspress dev",
|
|
105
|
+
"format": "biome format --write",
|
|
106
|
+
"test": "vitest --config=vitest.browser.config.ts --coverage.enabled --ui",
|
|
107
|
+
"test:ci": "vitest run --config=vitest.browser.config.ts",
|
|
108
|
+
"test:lib:ci": "vitest run",
|
|
109
|
+
"test:lib": "vitest --coverage.enabled --ui",
|
|
110
|
+
"script:gen-file": "esno scripts/gen-file.ts"
|
|
111
|
+
}
|
|
112
|
+
}
|