@zerodep-dev/vue-tsx-pro-tsx 0.0.0-stage → 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.
@@ -0,0 +1,174 @@
1
+ /**
2
+ * JSX 类型入口(tsconfig 的 jsxImportSource 指向本包时由 TypeScript 读取)。
3
+ *
4
+ * 在 Vue 自带的 JSX 类型(vue/jsx-runtime)上做三处调整:
5
+ * 1. 内容即 props:JSX 子节点按组件的 children prop 检查
6
+ * 2. _component 定义的组件:属性直接按 setup 参数的类型检查,泛型也能推导;
7
+ * 其他 Vue 组件(Transition、Teleport 等内置组件)的子节点照常走默认插槽
8
+ * 3. 原生元素的事件参数带上具体的 currentTarget:<input onInput={(e) => e.currentTarget.value} />
9
+ *
10
+ * 双向绑定写在下划线属性上:组件的可绑定 prop 本身以下划线开头(_value、_open),值是可写的变量或对象属性时
11
+ * 自动绑定,onUpdate:_value 按 prop 的类型检查;原生元素另有绑定专用的 _value、_checked、_open、_files(见文件末尾)。
12
+ */
13
+ // 顺带加载符文的全局类型:只要配置了 jsxImportSource,就能直接使用 _state 等
14
+ import type {} from './runes';
15
+ import type { VNodeChild } from 'vue';
16
+ import type { JSX as VueJSX } from 'vue/jsx-runtime';
17
+
18
+ /**
19
+ * _component 定义的组件:返回值带 '~tsx' 标记(见 runtime/component.ts)。
20
+ * Vue 的 FunctionalComponent 返回 any,要先排除,否则会被误判。
21
+ */
22
+ type IsTsxComponent<C> = C extends (...args: any) => infer R
23
+ ? 0 extends 1 & R
24
+ ? false
25
+ : R extends { readonly '~tsx': true }
26
+ ? true
27
+ : false
28
+ : false;
29
+
30
+ type ElementOf<Tag> = Tag extends keyof HTMLElementTagNameMap
31
+ ? HTMLElementTagNameMap[Tag]
32
+ : Tag extends keyof SVGElementTagNameMap
33
+ ? SVGElementTagNameMap[Tag]
34
+ : Element;
35
+
36
+ /** onXxx 事件处理函数的参数补上 currentTarget 的元素类型 */
37
+ type WithCurrentTarget<Attributes, El> = {
38
+ [Name in keyof Attributes]: Name extends `on${string}`
39
+ ? NonNullable<Attributes[Name]> extends (event: infer E) => unknown
40
+ ? (event: E & { currentTarget: El }) => void
41
+ : Attributes[Name]
42
+ : Attributes[Name];
43
+ };
44
+
45
+ type VueTag = keyof VueJSX.IntrinsicElements;
46
+ type UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (
47
+ value: infer I,
48
+ ) => void
49
+ ? I
50
+ : never;
51
+ type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
52
+
53
+ /** 某个属性在每个标签上的取值类型,各包一层元组,交叉时才不会把各标签的 undefined 等混在一起 */
54
+ type ValuePerTag<Tag, Name> = Tag extends VueTag
55
+ ? [VueJSX.IntrinsicElements[Tag][Name & keyof VueJSX.IntrinsicElements[Tag]]]
56
+ : never;
57
+
58
+ /**
59
+ * 可绑定的 prop(下划线开头)的更新回调:写了 onUpdate:_value 就由它处理更新,不再自动写回。
60
+ * 回调写成方法签名(参数双向协变,同 React 类型里事件回调的做法):值的类型是几种形态的联合时
61
+ * (单选是字符串、区间是对象),只处理其中一种的回调也能传。
62
+ * 泛型组件里把 props 原样转交给另一个泛型组件时,TypeScript 推不动依赖类型参数的这一层:展开的对象写成
63
+ * `{...(rest as ComboboxProps<T> & UpdateHandlers<ComboboxProps<T>>)}`
64
+ */
65
+ export type UpdateHandlers<P> = {
66
+ // Extract<keyof P, string> 而不是 keyof P:不在联合上分配,单选 / 多选两种形态的值并成一个参数类型(分配后回调推不出参数)
67
+ [K in Extract<keyof P, string> as K extends `_${string}` ? `onUpdate:${K}` : never]?: {
68
+ bivarianceHack(value: Exclude<P[K], undefined>): void;
69
+ }['bivarianceHack'];
70
+ };
71
+
72
+ /** 几个标签共有的属性,每个属性取所有标签都接受的值:<a> 与 <button> 的 type 取 'button' | 'submit' | 'reset' */
73
+ type SharedAttributes<Tag extends VueTag> = {
74
+ [Name in keyof VueJSX.IntrinsicElements[Tag]]: UnionToIntersection<ValuePerTag<Tag, Name>>[0];
75
+ };
76
+
77
+ /**
78
+ * 原生元素的属性。Tag 可以是几个标签的联合(一个工厂生成几个元素的组件、共用实现时,如 ui 的 H1–H6):
79
+ * 属性取这些标签共有的部分、取值是每个标签都接受的值,事件参数的 currentTarget 是这几种元素的联合,
80
+ * 所以属性能直接展开到 `<Tag {...props} />` 上(Tag 是同一组标签之一)。
81
+ */
82
+ export type IntrinsicProps<Tag extends VueTag> = WithCurrentTarget<
83
+ IsUnion<Tag> extends true ? SharedAttributes<Tag> : VueJSX.IntrinsicElements[Tag],
84
+ ElementOf<Tag>
85
+ > & { children?: VNodeChild };
86
+
87
+ export declare namespace JSX {
88
+ /** JSX 表达式的结果:同 Vue 的 VNode */
89
+ interface Element extends VueJSX.Element {}
90
+ /** 可以当标签用的类组件,同 Vue */
91
+ interface ElementClass extends VueJSX.ElementClass {}
92
+ /** 类组件的属性从哪里取,同 Vue */
93
+ interface ElementAttributesProperty extends VueJSX.ElementAttributesProperty {}
94
+ /** 子节点对应的 prop 名 */
95
+ interface ElementChildrenAttribute {
96
+ children: {};
97
+ }
98
+ /** 原生元素的属性:Vue 的定义加上 children 与精确的事件 currentTarget(见 IntrinsicProps) */
99
+ type IntrinsicElements = {
100
+ [Tag in keyof VueJSX.IntrinsicElements]: IntrinsicProps<Tag>;
101
+ };
102
+ /** 所有元素与组件都接受的属性(key、ref 等),同 Vue */
103
+ interface IntrinsicAttributes extends VueJSX.IntrinsicAttributes {}
104
+ /** 组件的属性类型:_component 按参数检查,其他 Vue 组件另外接受插槽内容 */
105
+ type LibraryManagedAttributes<C, P> = C extends string
106
+ ? P
107
+ : IsTsxComponent<C> extends true
108
+ ? // _component:setup 参数就是全部属性(子节点即 children prop),加上可绑定 prop 的 onUpdate:_x
109
+ P & UpdateHandlers<P>
110
+ : // 其他 Vue 组件:内容走默认插槽
111
+ P & { children?: unknown };
112
+ }
113
+
114
+ // Vue 3.5 的 HTML 属性类型还没有原生 Popover API 的属性,这里补上(只是类型,运行时原样交给浏览器)
115
+ declare module 'vue' {
116
+ interface HTMLAttributes {
117
+ /** 原生弹层:auto 自带 Esc 与点击外部关闭,manual 只由代码开关,hint 不关闭其他 auto 弹层 */
118
+ popover?: 'auto' | 'manual' | 'hint' | '' | undefined;
119
+ /** hidden="until-found" 的元素被页内搜索、锚点命中时触发,随后浏览器去掉 hidden */
120
+ onBeforematch?: (event: Event) => void;
121
+ }
122
+ interface ButtonHTMLAttributes {
123
+ /** 点击时开关的弹层元素 id */
124
+ popovertarget?: string | undefined;
125
+ /** 点击时对弹层做什么,默认 toggle */
126
+ popovertargetaction?: 'toggle' | 'show' | 'hide' | undefined;
127
+ /** 点击时对哪个元素下命令(Invoker Commands):目标元素的 id,配合 command */
128
+ commandfor?: string | undefined;
129
+ /**
130
+ * 下的命令:对话框 show-modal / close / request-close,弹层 toggle-popover / show-popover / hide-popover;
131
+ * 自定义命令以 -- 开头,目标元素在 command 事件里处理
132
+ */
133
+ command?:
134
+ | 'show-modal'
135
+ | 'close'
136
+ | 'request-close'
137
+ | 'toggle-popover'
138
+ | 'show-popover'
139
+ | 'hide-popover'
140
+ | `--${string}`
141
+ | undefined;
142
+ }
143
+ interface HTMLAttributes {
144
+ /** 收到按钮下的命令(Invoker Commands):event.command 是命令,event.source 是按下的按钮 */
145
+ onCommand?: (
146
+ event: Event & { readonly command: string; readonly source: Element | null },
147
+ ) => void;
148
+ }
149
+ interface DialogHTMLAttributes {
150
+ /** 哪些操作能关闭对话框:any(Esc、点外面)、closerequest(只有 Esc)、none(只能代码关) */
151
+ closedby?: 'any' | 'closerequest' | 'none' | undefined;
152
+ }
153
+ // 原生元素的双向绑定:_value={x} 编译成 v-model,其余在元素事件之后读回(见 compiler/bind.ts)
154
+ interface InputHTMLAttributes {
155
+ /** 双向绑定输入的值:_value={x},type="number" / "range" 自动按数字;修饰符写 _value={_bind.trim(x)} */
156
+ _value?: unknown;
157
+ /** 双向绑定勾选:复选框绑布尔,或绑数组(多选组:勾选时把 value 加进数组) */
158
+ _checked?: unknown;
159
+ /** 选中的文件(文件输入):_files={x},只读,用户选了文件后写回 x */
160
+ _files?: FileList | null | undefined;
161
+ }
162
+ interface TextareaHTMLAttributes {
163
+ /** 双向绑定输入的值:_value={x} */
164
+ _value?: unknown;
165
+ }
166
+ interface SelectHTMLAttributes {
167
+ /** 双向绑定选中的值:_value={x}(多选时绑数组) */
168
+ _value?: unknown;
169
+ }
170
+ interface DetailsHTMLAttributes {
171
+ /** 双向绑定展开状态:代码设置会展开,用户展开收起后写回 */
172
+ _open?: boolean | undefined;
173
+ }
174
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * 符文的类型:在类型层面它们都是“普通值”,编译后才变成 Vue 的 Ref。
3
+ */
4
+ import type { Ref } from 'vue';
5
+
6
+ /**
7
+ * _fromRef 解构时只接受 Ref 属性:编译后每个解构出的变量都按 .value 读写,
8
+ * 函数等非 Ref 属性会变成 never,提示从原对象单独解构,例如 const { reset } = useX()
9
+ */
10
+ type UnwrapRefs<T> = { [K in keyof T]: T[K] extends Ref<infer V> ? V : never };
11
+
12
+ /** 原生输入框绑定的修饰符(可以连写:_bind.lazy.trim);类型上就是传入的值本身 */
13
+ interface BindModifiers {
14
+ /** 去掉首尾空白 */
15
+ readonly trim: BindModifier;
16
+ /** 转成数字(<input type="number"> 与 type="range" 不用写,自动按数字) */
17
+ readonly number: BindModifier;
18
+ /** change 时才同步,而不是每次输入 */
19
+ readonly lazy: BindModifier;
20
+ }
21
+
22
+ /** 一个修饰符:_value={_bind.trim(name)},参数是可写的变量或对象属性 */
23
+ interface BindModifier extends BindModifiers {
24
+ <T>(target: T): T;
25
+ }
26
+
27
+ declare global {
28
+ /**
29
+ * 响应式状态 → ref,之后像普通变量一样读写。
30
+ * let 可以整体替换;const 不能整体替换,对象的属性照常可改。
31
+ * 跨文件共享:`export const settings = _state({...})` 可以直接导出(导出的是 ref 里的对象本身)。
32
+ */
33
+ function _state<T>(initial: T): T;
34
+ /** 没有初始值的响应式状态 → ref(),初始为 undefined */
35
+ function _state<T>(): T | undefined;
36
+ namespace _state {
37
+ /** 只能整体替换的状态 → shallowRef */
38
+ function raw<T>(initial: T): T;
39
+ /** 没有初始值、只能整体替换的状态 → shallowRef():DOM 元素、第三方实例 */
40
+ function raw<T>(): T | undefined;
41
+ /** 去掉响应式代理的静态深拷贝,交给 structuredClone、第三方库或日志 */
42
+ function snapshot<T>(value: T): T;
43
+ }
44
+
45
+ /**
46
+ * 派生值。const → computed,只读;let → 可以临时覆盖(乐观更新),依赖变化后重新跟随。
47
+ */
48
+ function _derived<T>(expression: T): T;
49
+ namespace _derived {
50
+ /** 复杂派生:派生逻辑写成函数,其余同 _derived */
51
+ function by<T>(fn: () => T): T;
52
+ }
53
+
54
+ /**
55
+ * 副作用(同 Svelte 的 $effect):挂载后、每次 DOM 更新后执行,读到的响应式变量自动成为依赖。
56
+ * 返回的函数在下次执行前、销毁时调用。只用于副作用(DOM、订阅、日志);派生值请用 _derived。
57
+ * 只想监听指定的值时用 Vue 的 watch(() => x, cb)。
58
+ */
59
+ function _effect(fn: () => void | (() => void)): void;
60
+ namespace _effect {
61
+ /** DOM 更新前执行,例如自动滚动前先测量 */
62
+ function pre(fn: () => void | (() => void)): void;
63
+ /** 不随组件销毁的副作用作用域,返回清理函数;里面可以创建 _effect */
64
+ function root(fn: () => void | (() => void)): () => void;
65
+ }
66
+
67
+ /**
68
+ * 组件参数默认值:可双向绑定的 prop,键名以下划线开头、本地变量不带下划线:({ _checked: checked = _bindable(false) });
69
+ * 父级传可写的变量或对象属性就双向绑定(_checked={on}),传别的值只是初始值或受控值,都不传就是组件自己的状态
70
+ */
71
+ function _bindable<T>(fallback?: T): T;
72
+
73
+ /**
74
+ * 原生输入框绑定的修饰符:<input _value={_bind.trim(name)} />、<input _value={_bind.lazy(note)} />。
75
+ * 一般的双向绑定不用它:下划线属性的值是可写的变量或对象属性时自动绑定(<Switch _checked={on} />)
76
+ */
77
+ const _bind: BindModifiers;
78
+
79
+ /**
80
+ * Ref → 变量:持有外部 Ref(组合式函数、storeToRefs 的返回值),之后像普通变量一样读写,写入直接落到原 Ref。
81
+ * 与 _toRef 互逆:`let theme = _fromRef(useStorage('theme', 'dark'))`、`const { x, y } = _fromRef(useMouse())`
82
+ */
83
+ function _fromRef<T>(source: Ref<T>): T;
84
+ /** 解构持有对象里的多个 Ref:`const { x, y } = _fromRef(useMouse())`,每个变量读写对应的 Ref */
85
+ function _fromRef<T extends object>(source: T): UnwrapRefs<T>;
86
+
87
+ /**
88
+ * 变量 → Ref:取回响应式变量背后的 Ref,交给需要可写 Ref 的 API。与 _fromRef 互逆:`useIncrement(_toRef(count))`。
89
+ * 只读的场合传 getter 就够了:`watch(() => count, …)`
90
+ */
91
+ function _toRef<T>(value: T): Ref<T>;
92
+
93
+ /**
94
+ * 只取当前值、不追踪(同 Svelte 的 untrack):写在 _effect、_derived 里时不会因为它重新执行;
95
+ * 写在组件函数体里表示有意只要初始值,例如 `let collapsed = _state(_untrack(!open))`,编译器不再警告
96
+ */
97
+ function _untrack<T>(value: T): T;
98
+ }
99
+
100
+ export {};