feat: initial commit - Phase 1 & 2 core features

This commit is contained in:
hiderfong
2026-04-22 17:07:33 +08:00
commit 1773bda06b
25005 changed files with 6252106 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
import { IfNever, IfUnknown, UnknownToNever, Writable, WritableArray } from "./util.js";
import { buildProp, buildProps, definePropType, epPropKey, isEpProp } from "./runtime.js";
import { EpProp, EpPropConvert, EpPropFinalized, EpPropInput, EpPropInputDefault, EpPropMergeType, ExtractPropType, IfEpProp, IfNativePropType, NativePropType, ResolvePropType } from "./types.js";
export { EpProp, EpPropConvert, EpPropFinalized, EpPropInput, EpPropInputDefault, EpPropMergeType, ExtractPropType, IfEpProp, IfNativePropType, IfNever, IfUnknown, NativePropType, ResolvePropType, UnknownToNever, Writable, WritableArray, buildProp, buildProps, definePropType, epPropKey, isEpProp };
+3
View File
@@ -0,0 +1,3 @@
import { buildProp, buildProps, definePropType, epPropKey, isEpProp } from "./runtime.mjs";
export { buildProp, buildProps, definePropType, epPropKey, isEpProp };
+33
View File
@@ -0,0 +1,33 @@
import { EpProp, EpPropConvert, EpPropFinalized, EpPropInput, EpPropMergeType, IfEpProp, IfNativePropType, NativePropType } from "./types.js";
import { PropType } from "vue";
//#region ../../packages/utils/vue/props/runtime.d.ts
declare const epPropKey = "__epPropKey";
declare const definePropType: <T>(val: any) => PropType<T>;
declare const isEpProp: (val: unknown) => val is EpProp<any, any, any>;
/**
* @description Build prop. It can better optimize prop types
* @description 生成 prop,能更好地优化类型
* @example
// limited options
// the type will be PropType<'light' | 'dark'>
buildProp({
type: String,
values: ['light', 'dark'],
} as const)
* @example
// limited options and other types
// the type will be PropType<'small' | 'large' | number>
buildProp({
type: [String, Number],
values: ['small', 'large'],
validator: (val: unknown): val is number => typeof val === 'number',
} as const)
@link see more: https://github.com/element-plus/element-plus/pull/3341
*/
declare const buildProp: <Type = never, Value = never, Validator = never, Default extends EpPropMergeType<Type, Value, Validator> = never, Required extends boolean = false>(prop: EpPropInput<Type, Value, Validator, Default, Required>, key?: string) => EpPropFinalized<Type, Value, Validator, Default, Required>;
declare const buildProps: <Props extends Record<string, {
[epPropKey]: true;
} | NativePropType | EpPropInput<any, any, any, any, any>>>(props: Props) => { [K in keyof Props]: IfEpProp<Props[K], Props[K], IfNativePropType<Props[K], Props[K], EpPropConvert<Props[K]>>> };
//#endregion
export { buildProp, buildProps, definePropType, epPropKey, isEpProp };
+60
View File
@@ -0,0 +1,60 @@
import { isObject } from "../../types.mjs";
import { hasOwn } from "../../objects.mjs";
import { fromPairs } from "lodash-unified";
import { warn } from "vue";
//#region ../../packages/utils/vue/props/runtime.ts
const epPropKey = "__epPropKey";
const definePropType = (val) => val;
const isEpProp = (val) => isObject(val) && !!val[epPropKey];
/**
* @description Build prop. It can better optimize prop types
* @description 生成 prop,能更好地优化类型
* @example
// limited options
// the type will be PropType<'light' | 'dark'>
buildProp({
type: String,
values: ['light', 'dark'],
} as const)
* @example
// limited options and other types
// the type will be PropType<'small' | 'large' | number>
buildProp({
type: [String, Number],
values: ['small', 'large'],
validator: (val: unknown): val is number => typeof val === 'number',
} as const)
@link see more: https://github.com/element-plus/element-plus/pull/3341
*/
const buildProp = (prop, key) => {
if (!isObject(prop) || isEpProp(prop)) return prop;
const { values, required, default: defaultValue, type, validator } = prop;
const epProp = {
type,
required: !!required,
validator: values || validator ? (val) => {
let valid = false;
let allowedValues = [];
if (values) {
allowedValues = Array.from(values);
if (hasOwn(prop, "default")) allowedValues.push(defaultValue);
valid ||= allowedValues.includes(val);
}
if (validator) valid ||= validator(val);
if (!valid && allowedValues.length > 0) {
const allowValuesText = [...new Set(allowedValues)].map((value) => JSON.stringify(value)).join(", ");
warn(`Invalid prop: validation failed${key ? ` for prop "${key}"` : ""}. Expected one of [${allowValuesText}], got value ${JSON.stringify(val)}.`);
}
return valid;
} : void 0,
[epPropKey]: true
};
if (hasOwn(prop, "default")) epProp.default = defaultValue;
return epProp;
};
const buildProps = (props) => fromPairs(Object.entries(props).map(([key, option]) => [key, buildProp(option, key)]));
//#endregion
export { buildProp, buildProps, definePropType, epPropKey, isEpProp };
//# sourceMappingURL=runtime.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../../../../packages/utils/vue/props/runtime.ts"],"sourcesContent":["import { warn } from 'vue'\nimport { fromPairs } from 'lodash-unified'\nimport { isObject } from '../../types'\nimport { hasOwn } from '../../objects'\n\nimport type { PropType } from 'vue'\nimport type {\n EpProp,\n EpPropConvert,\n EpPropFinalized,\n EpPropInput,\n EpPropMergeType,\n IfEpProp,\n IfNativePropType,\n NativePropType,\n} from './types'\n\nexport const epPropKey = '__epPropKey'\n\nexport const definePropType = <T>(val: any): PropType<T> => val\n\nexport const isEpProp = (val: unknown): val is EpProp<any, any, any> =>\n isObject(val) && !!(val as any)[epPropKey]\n\n/**\n * @description Build prop. It can better optimize prop types\n * @description 生成 prop,能更好地优化类型\n * @example\n // limited options\n // the type will be PropType<'light' | 'dark'>\n buildProp({\n type: String,\n values: ['light', 'dark'],\n } as const)\n * @example\n // limited options and other types\n // the type will be PropType<'small' | 'large' | number>\n buildProp({\n type: [String, Number],\n values: ['small', 'large'],\n validator: (val: unknown): val is number => typeof val === 'number',\n } as const)\n @link see more: https://github.com/element-plus/element-plus/pull/3341\n */\nexport const buildProp = <\n Type = never,\n Value = never,\n Validator = never,\n Default extends EpPropMergeType<Type, Value, Validator> = never,\n Required extends boolean = false,\n>(\n prop: EpPropInput<Type, Value, Validator, Default, Required>,\n key?: string\n): EpPropFinalized<Type, Value, Validator, Default, Required> => {\n // filter native prop type and nested prop, e.g `null`, `undefined` (from `buildProps`)\n if (!isObject(prop) || isEpProp(prop)) return prop as any\n\n const { values, required, default: defaultValue, type, validator } = prop\n\n const _validator =\n values || validator\n ? (val: unknown) => {\n let valid = false\n let allowedValues: unknown[] = []\n\n if (values) {\n allowedValues = Array.from(values)\n if (hasOwn(prop, 'default')) {\n allowedValues.push(defaultValue)\n }\n valid ||= allowedValues.includes(val)\n }\n if (validator) valid ||= validator(val)\n\n if (!valid && allowedValues.length > 0) {\n const allowValuesText = [...new Set(allowedValues)]\n .map((value) => JSON.stringify(value))\n .join(', ')\n warn(\n `Invalid prop: validation failed${\n key ? ` for prop \"${key}\"` : ''\n }. Expected one of [${allowValuesText}], got value ${JSON.stringify(\n val\n )}.`\n )\n }\n return valid\n }\n : undefined\n\n const epProp: any = {\n type,\n required: !!required,\n validator: _validator,\n [epPropKey]: true,\n }\n if (hasOwn(prop, 'default')) epProp.default = defaultValue\n return epProp\n}\n\nexport const buildProps = <\n Props extends Record<\n string,\n | { [epPropKey]: true }\n | NativePropType\n | EpPropInput<any, any, any, any, any>\n >,\n>(\n props: Props\n): {\n [K in keyof Props]: IfEpProp<\n Props[K],\n Props[K],\n IfNativePropType<Props[K], Props[K], EpPropConvert<Props[K]>>\n >\n} =>\n fromPairs(\n Object.entries(props).map(([key, option]) => [\n key,\n buildProp(option as any, key),\n ])\n ) as any\n"],"mappings":";;;;;;AAiBA,MAAa,YAAY;AAEzB,MAAa,kBAAqB,QAA0B;AAE5D,MAAa,YAAY,QACvB,SAAS,IAAI,IAAI,CAAC,CAAE,IAAY;;;;;;;;;;;;;;;;;;;;;AAsBlC,MAAa,aAOX,MACA,QAC+D;AAE/D,KAAI,CAAC,SAAS,KAAK,IAAI,SAAS,KAAK,CAAE,QAAO;CAE9C,MAAM,EAAE,QAAQ,UAAU,SAAS,cAAc,MAAM,cAAc;CAiCrE,MAAM,SAAc;EAClB;EACA,UAAU,CAAC,CAAC;EACZ,WAjCA,UAAU,aACL,QAAiB;GAChB,IAAI,QAAQ;GACZ,IAAI,gBAA2B,EAAE;AAEjC,OAAI,QAAQ;AACV,oBAAgB,MAAM,KAAK,OAAO;AAClC,QAAI,OAAO,MAAM,UAAU,CACzB,eAAc,KAAK,aAAa;AAElC,cAAU,cAAc,SAAS,IAAI;;AAEvC,OAAI,UAAW,WAAU,UAAU,IAAI;AAEvC,OAAI,CAAC,SAAS,cAAc,SAAS,GAAG;IACtC,MAAM,kBAAkB,CAAC,GAAG,IAAI,IAAI,cAAc,CAAC,CAChD,KAAK,UAAU,KAAK,UAAU,MAAM,CAAC,CACrC,KAAK,KAAK;AACb,SACE,kCACE,MAAM,cAAc,IAAI,KAAK,GAC9B,qBAAqB,gBAAgB,eAAe,KAAK,UACxD,IACD,CAAC,GACH;;AAEH,UAAO;MAET;GAMH,YAAY;EACd;AACD,KAAI,OAAO,MAAM,UAAU,CAAE,QAAO,UAAU;AAC9C,QAAO;;AAGT,MAAa,cAQX,UAQA,UACE,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,KAAK,YAAY,CAC3C,KACA,UAAU,QAAe,IAAI,CAC9B,CAAC,CACH"}
+123
View File
@@ -0,0 +1,123 @@
import { IfNever, UnknownToNever, WritableArray } from "./util.js";
import { epPropKey } from "./runtime.js";
import { ExtractPropTypes, PropType } from "vue";
//#region ../../packages/utils/vue/props/types.d.ts
type Value<T> = T[keyof T];
/**
* Extract the type of a single prop
*
* 提取单个 prop 的参数类型
*
* @example
* ExtractPropType<{ type: StringConstructor }> => string | undefined
* ExtractPropType<{ type: StringConstructor, required: true }> => string
* ExtractPropType<{ type: BooleanConstructor }> => boolean
*/
type ExtractPropType<T extends object> = Value<ExtractPropTypes<{
key: T;
}>>;
/**
* Extracts types via `ExtractPropTypes`, accepting `PropType<T>`, `XXXConstructor`, `never`...
*
* 通过 `ExtractPropTypes` 提取类型,接受 `PropType<T>`、`XXXConstructor`、`never`...
*
* @example
* ResolvePropType<BooleanConstructor> => boolean
* ResolvePropType<PropType<T>> => T
**/
type ResolvePropType<T> = IfNever<T, never, ExtractPropType<{
type: WritableArray<T>;
required: true;
}>>;
/**
* Merge Type, Value, Validator types
* 合并 Type、Value、Validator 的类型
*
* @example
* EpPropMergeType<StringConstructor, '1', 1> => 1 | "1" // ignores StringConstructor
* EpPropMergeType<StringConstructor, never, number> => string | number
*/
type EpPropMergeType<Type, Value, Validator> = IfNever<UnknownToNever<Value>, ResolvePropType<Type>, never> | UnknownToNever<Value> | UnknownToNever<Validator>;
/**
* Handling default values for input (constraints)
*
* 处理输入参数的默认值(约束)
*/
type EpPropInputDefault<Required extends boolean, Default> = Required extends true ? never : Default extends Record<string, unknown> | Array<any> ? () => Default : (() => Default) | Default;
/**
* Native prop types, e.g: `BooleanConstructor`, `StringConstructor`, `null`, `undefined`, etc.
*
* 原生 prop `类型,BooleanConstructor`、`StringConstructor`、`null`、`undefined` 等
*/
type NativePropType = ((...args: any) => any) | {
new (...args: any): any;
} | undefined | null;
type IfNativePropType<T, Y, N> = [T] extends [NativePropType] ? Y : N;
/**
* input prop `buildProp` or `buildProps` (constraints)
*
* prop 输入参数(约束)
*
* @example
* EpPropInput<StringConstructor, 'a', never, never, true>
* ⬇️
* {
type?: StringConstructor | undefined;
required?: true | undefined;
values?: readonly "a"[] | undefined;
validator?: ((val: any) => boolean) | ((val: any) => val is never) | undefined;
default?: undefined;
}
*/
type EpPropInput<Type, Value, Validator, Default extends EpPropMergeType<Type, Value, Validator>, Required extends boolean> = {
type?: Type;
required?: Required;
values?: readonly Value[];
validator?: ((val: any) => val is Validator) | ((val: any) => boolean);
default?: EpPropInputDefault<Required, Default>;
};
/**
* output prop `buildProp` or `buildProps`.
*
* prop 输出参数。
*
* @example
* EpProp<'a', 'b', true>
* ⬇️
* {
readonly type: PropType<"a">;
readonly required: true;
readonly validator: ((val: unknown) => boolean) | undefined;
readonly default: "b";
__epPropKey: true;
}
*/
type EpProp<Type, Default, Required> = {
readonly type: PropType<Type>;
readonly required: [Required] extends [true] ? true : false;
readonly validator: ((val: unknown) => boolean) | undefined;
[epPropKey]: true;
} & IfNever<Default, unknown, {
readonly default: Default;
}>;
/**
* Determine if it is `EpProp`
*/
type IfEpProp<T, Y, N> = T extends {
[epPropKey]: true;
} ? Y : N;
/**
* Converting input to output.
*
* 将输入转换为输出
*/
type EpPropConvert<Input> = Input extends EpPropInput<infer Type, infer Value, infer Validator, any, infer Required> ? EpPropFinalized<Type, Value, Validator, Input['default'], Required> : never;
/**
* Finalized conversion output
*
* 最终转换 EpProp
*/
type EpPropFinalized<Type, Value, Validator, Default, Required> = EpProp<EpPropMergeType<Type, Value, Validator>, UnknownToNever<Default>, Required>;
//#endregion
export { EpProp, EpPropConvert, EpPropFinalized, EpPropInput, EpPropInputDefault, EpPropMergeType, ExtractPropType, IfEpProp, IfNativePropType, NativePropType, ResolvePropType };
View File
+8
View File
@@ -0,0 +1,8 @@
//#region ../../packages/utils/vue/props/util.d.ts
type Writable<T> = { -readonly [P in keyof T]: T[P] };
type WritableArray<T> = T extends readonly any[] ? Writable<T> : T;
type IfNever<T, Y = true, N = false> = [T] extends [never] ? Y : N;
type IfUnknown<T, Y, N> = [unknown] extends [T] ? Y : N;
type UnknownToNever<T> = IfUnknown<T, never, T>;
//#endregion
export { IfNever, IfUnknown, UnknownToNever, Writable, WritableArray };
View File