Skip to content

Commit 56b3ab6

Browse files
committed
feat: v1.0.0-rc.1
1 parent 6f6151a commit 56b3ab6

17 files changed

+506
-385
lines changed

.vscode/settings.json

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
{
2+
"typescript.tsdk": "node_modules/typescript/lib",
23
"editor.defaultFormatter": "esbenp.prettier-vscode",
3-
"editor.detectIndentation": false,
4-
"editor.formatOnSave": true,
5-
"editor.formatOnType": true,
6-
"editor.codeActionsOnSave": {
7-
"source.fixAll": "explicit"
8-
},
9-
"typescript.tsdk": "node_modules/typescript/lib"
4+
"editor.formatOnSave": true
105
}

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "veact",
3-
"version": "1.0.0-beta.2",
3+
"version": "1.0.0-rc.1",
44
"description": "Mutable state enhancer library for React by @vue/reactivity",
55
"keywords": [
66
"React",
@@ -50,7 +50,7 @@
5050
"react-dom": "^16.8.0 || ^17 || ^18 || ^19"
5151
},
5252
"dependencies": {
53-
"@vue/reactivity": "^3.5.0-beta.2"
53+
"@vue/reactivity": "^3.5.0-rc.1"
5454
},
5555
"devDependencies": {
5656
"@eslint/js": "^9.x",
@@ -64,7 +64,7 @@
6464
"eslint-config-prettier": "^9.x",
6565
"eslint-plugin-prettier": "^5.x",
6666
"globals": "^15.9.0",
67-
"jsdom": "^24.x",
67+
"jsdom": "^25.x",
6868
"prettier": "^3.x",
6969
"react": "^18.x",
7070
"react-dom": "^18.x",

pnpm-lock.yaml

+298-358
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/computed.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { useState as useReactState } from 'react'
7-
import { useWatch } from './watch/watch'
7+
import { useWatch } from './watch'
88
import { useForceUpdate } from './_utils'
99
import { computed as vComputed } from '@vue/reactivity'
1010
import type {

src/index.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
// redirect all APIs from @vue/reactivity
77
export * from '@vue/reactivity'
8+
export { watch as baseWatch } from '@vue/reactivity'
89

910
// lifecycle hooks
1011
export { onMounted, onUpdated, onBeforeUnmount } from './lifecycle'
@@ -22,15 +23,12 @@ export { useReadonly, useShallowReadonly } from './readonly'
2223
export { useComputed } from './computed'
2324

2425
// watch and hooks
25-
export { watch, useWatch } from './watch/watch'
26-
export type { WatchOptions, WatchSource, MultiWatchSources, WatchCallback } from './watch/watch'
26+
export { watch, useWatch } from './watch'
27+
export type { WatchOptions, MultiWatchSources } from './watch'
2728

2829
// watchEffect and hooks
29-
export { watchEffect, useWatchEffect } from './watch/watchEffect'
30-
export type { WatchEffect, WatchEffectOptions } from './watch/watchEffect'
31-
32-
// watch handle
33-
export type { WatchStopHandle, WatchHandle } from './watch/type'
30+
export { watchEffect, useWatchEffect } from './watchEffect'
31+
export type { WatchEffectOptions } from './watchEffect'
3432

3533
// effectScope hooks
3634
export { useEffectScope } from './effectScope'

src/reactive.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { useState as useReactState } from 'react'
7-
import { useWatch } from './watch/watch'
7+
import { useWatch } from './watch'
88
import { useForceUpdate } from './_utils'
99
import { reactive as vReactive, shallowReactive as vShallowReactive } from '@vue/reactivity'
1010
import type { Reactive, ShallowReactive } from '@vue/reactivity'

src/reactivity.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @author Surmon <https://github.com/surmon-china>
44
*/
55

6-
import { useWatch } from './watch/watch'
6+
import { useWatch } from './watch'
77
import { useForceUpdate } from './_utils'
88

99
/**

src/readonly.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { useState as useReactState } from 'react'
7-
import { useWatch } from './watch/watch'
7+
import { useWatch } from './watch'
88
import { useForceUpdate } from './_utils'
99
import { readonly as vReadonly, shallowReadonly as vShallowReadonly } from '@vue/reactivity'
1010
import type { DeepReadonly, UnwrapNestedRefs } from '@vue/reactivity'

src/ref.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { useState as useReactState } from 'react'
77
import { useForceUpdate, IfAny } from './_utils'
8-
import { useWatch } from './watch/watch'
8+
import { useWatch } from './watch'
99
import { ref as vRef, shallowRef as vShallowRef, customRef as vCustomRef } from '@vue/reactivity'
1010
import type { Ref, UnwrapRef, ShallowRef, CustomRefFactory } from '@vue/reactivity'
1111

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/watch.ts

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* @module veact.watch
3+
* @author Surmon <https://github.com/surmon-china>
4+
*/
5+
6+
import { useState as useReactState } from 'react'
7+
import { watch as vueWatch } from '@vue/reactivity'
8+
import type {
9+
ReactiveMarker,
10+
DebuggerOptions,
11+
WatchCallback,
12+
WatchSource,
13+
WatchHandle,
14+
} from '@vue/reactivity'
15+
import { onBeforeUnmount } from './lifecycle'
16+
import { logger } from './_logger'
17+
18+
// changelog: https://github.com/vuejs/core/blob/main/CHANGELOG.md
19+
// https://github.com/vuejs/core/blob/main/packages/runtime-core/src/apiWatch.ts
20+
// https://github.com/vuejs/core/blob/main/packages/reactivity/src/watch.ts
21+
22+
export interface WatchOptions<Immediate = boolean> extends DebuggerOptions {
23+
immediate?: Immediate
24+
deep?: boolean | number
25+
once?: boolean
26+
// The `flush` option is not supported in react at the moment.
27+
// flush?: 'pre' | 'post' | 'sync'
28+
}
29+
30+
export type MultiWatchSources = (WatchSource<unknown> | object)[]
31+
32+
type MaybeUndefined<T, I> = I extends true ? T | undefined : T
33+
type MapSources<T, Immediate> = {
34+
[K in keyof T]: T[K] extends WatchSource<infer V>
35+
? MaybeUndefined<V, Immediate>
36+
: T[K] extends object
37+
? MaybeUndefined<T[K], Immediate>
38+
: never
39+
}
40+
41+
/**
42+
* Watches one or more reactive data sources and invokes a callback function when the sources change.
43+
*
44+
* @param source - The watcher's source.
45+
* @param callback - This function will be called when the source is changed.
46+
* @param options - An optional options object that does not support the `flush` option compared to Vue (3.5.0).
47+
* @see {@link https://vuejs.org/api/reactivity-core.html#watch Vue `watch()`}
48+
*
49+
* @example
50+
* ```js
51+
* const count = ref(0)
52+
* watch(count, (count, prevCount) => {
53+
* // ...
54+
* })
55+
* ```
56+
*/
57+
58+
// overload: single source + cb
59+
export function watch<T, Immediate extends Readonly<boolean> = false>(
60+
source: WatchSource<T>,
61+
callback: WatchCallback<T, MaybeUndefined<T, Immediate>>,
62+
options?: WatchOptions<Immediate>,
63+
): WatchHandle
64+
65+
// overload: reactive array or tuple of multiple sources + cb
66+
export function watch<T extends Readonly<MultiWatchSources>, Immediate extends Readonly<boolean> = false>(
67+
sources: readonly [...T] | T,
68+
callback: [T] extends [ReactiveMarker]
69+
? WatchCallback<T, MaybeUndefined<T, Immediate>>
70+
: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
71+
options?: WatchOptions<Immediate>,
72+
): WatchHandle
73+
74+
// overload: array of multiple sources + cb
75+
export function watch<T extends MultiWatchSources, Immediate extends Readonly<boolean> = false>(
76+
sources: [...T],
77+
callback: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
78+
options?: WatchOptions<Immediate>,
79+
): WatchHandle
80+
81+
// overload: watching reactive object w/ cb
82+
export function watch<T extends object, Immediate extends Readonly<boolean> = false>(
83+
source: T,
84+
callback: WatchCallback<T, MaybeUndefined<T, Immediate>>,
85+
options?: WatchOptions<Immediate>,
86+
): WatchHandle
87+
88+
// implementation
89+
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
90+
source: T | WatchSource<T>,
91+
callback: WatchCallback<T>,
92+
options: WatchOptions<Immediate> = {},
93+
): WatchHandle {
94+
return vueWatch(source as any, callback, {
95+
...options,
96+
onWarn: logger.warn,
97+
scheduler: (job) => job(),
98+
})
99+
}
100+
101+
/**
102+
* Watches one or more reactive data sources and invokes a callback function when the sources change.
103+
*
104+
* @param source - The watcher's source.
105+
* @param callback - This function will be called when the source is changed.
106+
* @param options - An optional options object that does not support the `flush` option compared to Vue (3.5.0).
107+
* @see {@link https://vuejs.org/api/reactivity-core.html#watch Vue `watch()`}
108+
*
109+
* @example
110+
* ```js
111+
* const count = useRef(0)
112+
* useWatch(count, (count, prevCount) => {
113+
* // ...
114+
* })
115+
* ```
116+
*/
117+
export const useWatch: typeof watch = (source: any, callback: any, options = {}) => {
118+
const [watchHandle] = useReactState(() => watch(source as any, callback, options))
119+
onBeforeUnmount(() => watchHandle.stop())
120+
return watchHandle
121+
}

src/watchEffect.ts

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* @module veact.watchEffect
3+
* @author Surmon <https://github.com/surmon-china>
4+
*/
5+
6+
import { useState as useReactState } from 'react'
7+
import { watch as vueWatch } from '@vue/reactivity'
8+
import type { WatchEffect, WatchHandle, DebuggerOptions } from '@vue/reactivity'
9+
import { onBeforeUnmount } from './lifecycle'
10+
import { logger } from './_logger'
11+
12+
// changelog: https://github.com/vuejs/core/blob/main/CHANGELOG.md
13+
// https://github.com/vuejs/core/blob/main/packages/runtime-core/src/apiWatch.ts
14+
// https://github.com/vuejs/core/blob/main/packages/reactivity/src/watch.ts
15+
16+
export type WatchEffectOptions = DebuggerOptions
17+
18+
/**
19+
* Runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.
20+
*
21+
* @param effectFn - The effect function to run.
22+
* @param options - An optional options object that can be used to adjust the effect's flush timing or to debug the effect's dependencies; the `flush` option is not supported compared to Vue (3.5.0).
23+
* @see {@link https://vuejs.org/api/reactivity-core.html#watcheffect Vue `watchEffect()`}
24+
*
25+
* @example
26+
* ```js
27+
* const count = ref(0)
28+
* watchEffect(() => console.log(count.value))
29+
* // -> logs 0
30+
*
31+
* count.value++
32+
* // -> logs 1
33+
* ```
34+
*/
35+
export function watchEffect(effectFn: WatchEffect, options: WatchEffectOptions = {}): WatchHandle {
36+
return vueWatch(effectFn, null, {
37+
...options,
38+
onWarn: logger.warn,
39+
scheduler: (job) => job(),
40+
})
41+
}
42+
43+
/**
44+
* Runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.
45+
*
46+
* @param effect - The effect function to run.
47+
* @param options - An optional options object that can be used to adjust the effect's flush timing or to debug the effect's dependencies; the `flush` option is not supported compared to Vue (3.5.0).
48+
* @see {@link https://vuejs.org/api/reactivity-core.html#watcheffect Vue `watchEffect()`}
49+
*
50+
* @example
51+
* ```js
52+
* const count = useRef(0)
53+
* useWatchEffect(() => console.log(count.value))
54+
* // -> logs 0
55+
*
56+
* count.value++
57+
* // -> logs 1
58+
* ```
59+
*/
60+
export const useWatchEffect: typeof watchEffect = (effect: any, options?: any) => {
61+
const [watchHandle] = useReactState(() => watchEffect(effect, options))
62+
onBeforeUnmount(() => watchHandle.stop())
63+
return watchHandle
64+
}

tests/apis.spec.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@ import {
2222
} from '../src'
2323

2424
test('<exports> should be export all @vue/reactivity members', () => {
25+
expect(vueReactivity.watch).toBe(veact.baseWatch)
2526
expect(
26-
Object.keys(vueReactivity).every((key) => {
27-
const targetMember = veact[key]
28-
return targetMember && vueReactivity[key] === targetMember
29-
}),
27+
Object.keys(vueReactivity)
28+
.filter((key) => key !== 'watch')
29+
.every((key) => {
30+
const targetMember = (veact as any)[key]
31+
return targetMember && (vueReactivity as any)[key] === targetMember
32+
}),
3033
).toBeTruthy()
3134
})
3235

tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
"noImplicitReturns": true,
1717
"outDir": "dist"
1818
},
19-
"exclude": ["node_modules", "dist", "dev", "tests"]
19+
"exclude": ["node_modules", "dist"]
2020
}

0 commit comments

Comments
 (0)