mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 02:44:45 +00:00
feat(configs/oxlint): add linter
This commit is contained in:
@@ -1 +1,28 @@
|
||||
# @robonen/vue
|
||||
# @robonen/vue
|
||||
|
||||
Collection of composables and utilities for Vue 3.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pnpm install @robonen/vue
|
||||
```
|
||||
|
||||
## Composables
|
||||
|
||||
| Category | Composables |
|
||||
| -------------- | ------------------------------------------------------------------ |
|
||||
| **browser** | `useEventListener`, `useFocusGuard`, `useSupported` |
|
||||
| **component** | `unrefElement`, `useRenderCount`, `useRenderInfo` |
|
||||
| **lifecycle** | `tryOnBeforeMount`, `tryOnMounted`, `tryOnScopeDispose`, `useMounted` |
|
||||
| **math** | `useClamp` |
|
||||
| **reactivity** | `broadcastedRef`, `useCached`, `useLastChanged`, `useSyncRefs` |
|
||||
| **state** | `useAppSharedState`, `useAsyncState`, `useContextFactory`, `useCounter`, `useInjectionStore`, `useToggle` |
|
||||
| **storage** | `useLocalStorage`, `useSessionStorage`, `useStorage`, `useStorageAsync` |
|
||||
| **utilities** | `useOffsetPagination` |
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { useToggle, useEventListener } from '@robonen/vue';
|
||||
```
|
||||
4
web/vue/oxlint.config.ts
Normal file
4
web/vue/oxlint.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { defineConfig } from 'oxlint';
|
||||
import { compose, base, typescript, vue, vitest, imports } from '@robonen/oxlint';
|
||||
|
||||
export default defineConfig(compose(base, typescript, vue, vitest, imports));
|
||||
@@ -32,13 +32,16 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "oxlint -c oxlint.config.ts",
|
||||
"test": "vitest run",
|
||||
"dev": "vitest dev",
|
||||
"build": "tsdown"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@robonen/oxlint": "workspace:*",
|
||||
"@robonen/tsconfig": "workspace:*",
|
||||
"@vue/test-utils": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"tsdown": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isArray, isString, noop, type Arrayable, type VoidFunction } from '@robonen/stdlib';
|
||||
import { isArray, isString, noop } from '@robonen/stdlib';
|
||||
import type { Arrayable, VoidFunction } from '@robonen/stdlib';
|
||||
import type { MaybeRefOrGetter } from 'vue';
|
||||
import { defaultWindow } from '@/types';
|
||||
|
||||
@@ -9,9 +10,7 @@ interface InferEventTarget<Events> {
|
||||
removeEventListener: (event: Events, listener?: any, options?: any) => any;
|
||||
}
|
||||
|
||||
export interface GeneralEventListener<E = Event> {
|
||||
(evt: E): void;
|
||||
}
|
||||
export type GeneralEventListener<E = Event> = (evt: E) => void;
|
||||
|
||||
export type WindowEventName = keyof WindowEventMap;
|
||||
export type DocumentEventName = keyof DocumentEventMap;
|
||||
@@ -84,7 +83,7 @@ export function useEventListener<Names extends string, EventType = Event>(
|
||||
event: Arrayable<Names>,
|
||||
listener: Arrayable<GeneralEventListener<EventType>>,
|
||||
options?: MaybeRefOrGetter<boolean | AddEventListenerOptions>
|
||||
)
|
||||
): VoidFunction;
|
||||
|
||||
/**
|
||||
* @name useEventListener
|
||||
@@ -104,13 +103,13 @@ export function useEventListener(...args: any[]) {
|
||||
let target: MaybeRefOrGetter<EventTarget> | undefined;
|
||||
let events: Arrayable<string>;
|
||||
let listeners: Arrayable<Function>;
|
||||
let options: MaybeRefOrGetter<boolean | AddEventListenerOptions> | undefined;
|
||||
let _options: MaybeRefOrGetter<boolean | AddEventListenerOptions> | undefined;
|
||||
|
||||
if (isString(args[0]) || isArray(args[0])) {
|
||||
[events, listeners, options] = args;
|
||||
[events, listeners, _options] = args;
|
||||
target = defaultWindow;
|
||||
} else {
|
||||
[target, events, listeners, options] = args;
|
||||
[target, events, listeners, _options] = args;
|
||||
}
|
||||
|
||||
if (!target)
|
||||
@@ -124,13 +123,16 @@ export function useEventListener(...args: any[]) {
|
||||
|
||||
const cleanups: Function[] = [];
|
||||
|
||||
const cleanup = () => {
|
||||
const _cleanup = () => {
|
||||
cleanups.forEach(fn => fn());
|
||||
cleanups.length = 0;
|
||||
}
|
||||
|
||||
const register = (el: any, event: string, listener: any, options: any) => {
|
||||
const _register = (el: any, event: string, listener: any, options: any) => {
|
||||
el.addEventListener(event, listener, options);
|
||||
return () => el.removeEventListener(event, listener, options);
|
||||
}
|
||||
|
||||
void _cleanup;
|
||||
void _register;
|
||||
}
|
||||
@@ -17,7 +17,7 @@ const setupFocusGuard = (namespace?: string) => {
|
||||
const getFocusGuards = (namespace: string) =>
|
||||
document.querySelectorAll(`[data-${namespace}]`);
|
||||
|
||||
describe('useFocusGuard', () => {
|
||||
describe(useFocusGuard, () => {
|
||||
let component: ReturnType<typeof setupFocusGuard>;
|
||||
const namespace = 'test-guard';
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('useFocusGuard', () => {
|
||||
component = setupFocusGuard(namespace);
|
||||
|
||||
const guards = getFocusGuards(namespace);
|
||||
expect(guards.length).toBe(2);
|
||||
expect(guards).toHaveLength(2);
|
||||
|
||||
guards.forEach((guard) => {
|
||||
expect(guard.getAttribute('tabindex')).toBe('0');
|
||||
@@ -46,7 +46,7 @@ describe('useFocusGuard', () => {
|
||||
|
||||
component.unmount();
|
||||
|
||||
expect(getFocusGuards(namespace).length).toBe(0);
|
||||
expect(getFocusGuards(namespace)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('correctly manage multiple instances with the same namespace', () => {
|
||||
@@ -54,16 +54,16 @@ describe('useFocusGuard', () => {
|
||||
const wrapper2 = setupFocusGuard(namespace);
|
||||
|
||||
// Guards should not be duplicated
|
||||
expect(getFocusGuards(namespace).length).toBe(2);
|
||||
expect(getFocusGuards(namespace)).toHaveLength(2);
|
||||
|
||||
wrapper1.unmount();
|
||||
|
||||
// Second instance still keeps the guards
|
||||
expect(getFocusGuards(namespace).length).toBe(2);
|
||||
expect(getFocusGuards(namespace)).toHaveLength(2);
|
||||
|
||||
wrapper2.unmount();
|
||||
|
||||
// No guards left after all instances are unmounted
|
||||
expect(getFocusGuards(namespace).length).toBe(0);
|
||||
expect(getFocusGuards(namespace)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,14 +11,14 @@ const ComponentStub = defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const isSupported = useSupported(() => props.location in window);
|
||||
const isSupported = useSupported(() => props.location in globalThis);
|
||||
|
||||
return { isSupported };
|
||||
},
|
||||
template: `<div>{{ isSupported }}</div>`,
|
||||
});
|
||||
|
||||
describe('useSupported', () => {
|
||||
describe(useSupported, () => {
|
||||
it('return whether the feature is supported', async () => {
|
||||
const component = mount(ComponentStub);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ export function useSupported(feature: () => unknown) {
|
||||
|
||||
return computed(() => {
|
||||
// add reactive dependency on isMounted
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
isMounted.value;
|
||||
|
||||
return Boolean(feature());
|
||||
|
||||
@@ -3,7 +3,7 @@ import { computed, defineComponent, nextTick, ref, shallowRef } from 'vue';
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { unrefElement } from '.';
|
||||
|
||||
describe('unrefElement', () => {
|
||||
describe(unrefElement, () => {
|
||||
it('returns a plain element when passed a raw element', () => {
|
||||
const htmlEl = document.createElement('div');
|
||||
const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
@@ -47,14 +47,14 @@ describe('unrefElement', () => {
|
||||
const result = unrefElement(childInstance);
|
||||
|
||||
expect(result).toBe(childInstance.$el);
|
||||
expect((result as HTMLElement).classList.contains('child-el')).toBe(true);
|
||||
expect((result as HTMLElement).classList.contains('child-el')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('handles null and undefined values', () => {
|
||||
expect(unrefElement(undefined)).toBe(undefined);
|
||||
expect(unrefElement(null)).toBe(null);
|
||||
expect(unrefElement(ref(null))).toBe(null);
|
||||
expect(unrefElement(ref(undefined))).toBe(undefined);
|
||||
expect(unrefElement(ref<null>(null))).toBe(null);
|
||||
expect(unrefElement(ref<undefined>(undefined))).toBe(undefined);
|
||||
expect(unrefElement(() => null)).toBe(null);
|
||||
expect(unrefElement(() => undefined)).toBe(undefined);
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ const ComponentStub = defineComponent({
|
||||
template: `<div>{{ visibleCount }}</div>`,
|
||||
});
|
||||
|
||||
describe('useRenderCount', () => {
|
||||
describe(useRenderCount, () => {
|
||||
it('return the number of times the component has been rendered', async () => {
|
||||
const component = mount(ComponentStub);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { onMounted, onUpdated, readonly, type ComponentInternalInstance } from 'vue';
|
||||
import { onMounted, onUpdated, readonly } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { useCounter } from '@/composables/state/useCounter';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ const UnnamedComponentStub = defineComponent({
|
||||
template: `<div>{{ visibleCount }}</div>`,
|
||||
});
|
||||
|
||||
describe('useRenderInfo', () => {
|
||||
describe(useRenderInfo, () => {
|
||||
it('return uid if component name is not available', async () => {
|
||||
const wrapper = mount(UnnamedComponentStub);
|
||||
|
||||
@@ -42,8 +42,8 @@ describe('useRenderInfo', () => {
|
||||
expect(wrapper.vm.info.duration.value).toBeGreaterThan(0);
|
||||
expect(wrapper.vm.info.lastRendered).toBeGreaterThan(0);
|
||||
|
||||
let lastRendered = wrapper.vm.info.lastRendered;
|
||||
let duration = wrapper.vm.info.duration.value;
|
||||
const lastRendered = wrapper.vm.info.lastRendered;
|
||||
const duration = wrapper.vm.info.duration.value;
|
||||
|
||||
// Will not trigger a render
|
||||
wrapper.vm.hiddenCount++;
|
||||
@@ -76,8 +76,8 @@ describe('useRenderInfo', () => {
|
||||
expect(info.duration.value).toBe(0);
|
||||
expect(info.lastRendered).toBeGreaterThan(0);
|
||||
|
||||
let lastRendered = info.lastRendered;
|
||||
let duration = info.duration.value;
|
||||
const lastRendered = info.lastRendered;
|
||||
const duration = info.duration.value;
|
||||
|
||||
// Will not trigger a render
|
||||
wrapper.vm.hiddenCount++;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { timestamp } from '@robonen/stdlib';
|
||||
import { onBeforeMount, onBeforeUpdate, onMounted, onUpdated, readonly, ref, type ComponentInternalInstance } from 'vue';
|
||||
import { onBeforeMount, onBeforeUpdate, onMounted, onUpdated, readonly, ref } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { useRenderCount } from '../useRenderCount';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
|
||||
@@ -24,7 +25,7 @@ export function useRenderInfo(instance?: ComponentInternalInstance) {
|
||||
const duration = ref(0);
|
||||
let renderStartTime = 0;
|
||||
|
||||
const startMark = () => renderStartTime = performance.now();
|
||||
const startMark = () => { renderStartTime = performance.now(); };
|
||||
const endMark = () => {
|
||||
duration.value = Math.max(performance.now() - renderStartTime, 0);
|
||||
renderStartTime = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { onBeforeMount, nextTick, type ComponentInternalInstance } from 'vue';
|
||||
import { onBeforeMount, nextTick } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, it, vi, expect } from 'vitest';
|
||||
import { defineComponent, nextTick, type PropType } from 'vue';
|
||||
import { defineComponent, nextTick } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { tryOnMounted } from '.';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
@@ -11,12 +12,12 @@ const ComponentStub = defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
props.callback && tryOnMounted(props.callback);
|
||||
if (props.callback) { tryOnMounted(props.callback); }
|
||||
},
|
||||
template: `<div></div>`,
|
||||
});
|
||||
|
||||
describe('tryOnMounted', () => {
|
||||
describe(tryOnMounted, () => {
|
||||
it('run the callback when mounted', () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { onMounted, nextTick, type ComponentInternalInstance } from 'vue';
|
||||
import { onMounted, nextTick } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { defineComponent, effectScope, type PropType } from 'vue';
|
||||
import { defineComponent, effectScope } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { tryOnScopeDispose } from '.';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
@@ -17,12 +18,12 @@ const ComponentStub = defineComponent({
|
||||
template: '<div></div>',
|
||||
});
|
||||
|
||||
describe('tryOnScopeDispose', () => {
|
||||
describe(tryOnScopeDispose, () => {
|
||||
it('returns false when the scope is not active', () => {
|
||||
const callback = vi.fn();
|
||||
const detectedScope = tryOnScopeDispose(callback);
|
||||
|
||||
expect(detectedScope).toBe(false);
|
||||
expect(detectedScope).toBeFalsy();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -35,7 +36,7 @@ describe('tryOnScopeDispose', () => {
|
||||
detectedScope = tryOnScopeDispose(callback);
|
||||
});
|
||||
|
||||
expect(detectedScope).toBe(true);
|
||||
expect(detectedScope).toBeTruthy();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
scope.stop();
|
||||
|
||||
@@ -12,7 +12,7 @@ const ComponentStub = defineComponent({
|
||||
template: `<div>{{ isMounted }}</div>`,
|
||||
});
|
||||
|
||||
describe('useMounted', () => {
|
||||
describe(useMounted, () => {
|
||||
it('return the mounted state of the component', async () => {
|
||||
const component = mount(ComponentStub);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { onMounted, readonly, ref, type ComponentInternalInstance } from 'vue';
|
||||
import { onMounted, readonly, ref } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
|
||||
/**
|
||||
@@ -21,7 +22,7 @@ export function useMounted(instance?: ComponentInternalInstance) {
|
||||
const isMounted = ref(false);
|
||||
const targetInstance = getLifeCycleTarger(instance);
|
||||
|
||||
onMounted(() => isMounted.value = true, targetInstance);
|
||||
onMounted(() => { isMounted.value = true; }, targetInstance);
|
||||
|
||||
return readonly(isMounted);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ref, readonly, computed } from 'vue';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { useClamp } from '.';
|
||||
|
||||
describe('useClamp', () => {
|
||||
describe(useClamp, () => {
|
||||
it('non-reactive values should be clamped', () => {
|
||||
const clampedValue = useClamp(10, 0, 5);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { clamp, isFunction } from '@robonen/stdlib';
|
||||
import { computed, isReadonly, ref, toValue, type ComputedRef, type MaybeRef, type MaybeRefOrGetter, type WritableComputedRef } from 'vue';
|
||||
import { computed, isReadonly, ref, toValue } from 'vue';
|
||||
import type { ComputedRef, MaybeRef, MaybeRefOrGetter, WritableComputedRef } from 'vue';
|
||||
|
||||
/**
|
||||
* @name useClamp
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useCached } from '.';
|
||||
|
||||
const arrayEquals = (a: number[], b: number[]) => a.length === b.length && a.every((v, i) => v === b[i]);
|
||||
|
||||
describe('useCached', () => {
|
||||
describe(useCached, () => {
|
||||
it('default comparator', async () => {
|
||||
const externalValue = ref(0);
|
||||
const cachedValue = useCached(externalValue);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ref, watch, toValue, type MaybeRefOrGetter, type Ref, type WatchOptions } from 'vue';
|
||||
import { ref, watch, toValue } from 'vue';
|
||||
import type { MaybeRefOrGetter, Ref, WatchOptions } from 'vue';
|
||||
|
||||
export type Comparator<Value> = (a: Value, b: Value) => boolean;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { useLastChanged } from '.';
|
||||
import { timestamp } from '@robonen/stdlib';
|
||||
|
||||
describe('useLastChanged', () => {
|
||||
describe(useLastChanged, () => {
|
||||
it('initialize with null if no initialValue is provided', () => {
|
||||
const source = ref(0);
|
||||
const lastChanged = useLastChanged(source);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { timestamp } from '@robonen/stdlib';
|
||||
import { ref, watch, type WatchSource, type WatchOptions, type Ref } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import type { WatchSource, WatchOptions, Ref } from 'vue';
|
||||
|
||||
export interface UseLastChangedOptions<
|
||||
Immediate extends boolean,
|
||||
@@ -32,7 +33,7 @@ export function useLastChanged(source: WatchSource, options: UseLastChangedOptio
|
||||
export function useLastChanged(source: WatchSource, options: UseLastChangedOptions<boolean, any> = {}): Ref<number | null> | Ref<number> {
|
||||
const lastChanged = ref<number | null>(options.initialValue ?? null);
|
||||
|
||||
watch(source, () => lastChanged.value = timestamp(), options);
|
||||
watch(source, () => { lastChanged.value = timestamp(); }, options);
|
||||
|
||||
return lastChanged;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
import { useSyncRefs } from '.';
|
||||
|
||||
describe('useSyncRefs', () => {
|
||||
describe(useSyncRefs, () => {
|
||||
it('sync the value of a source ref with multiple target refs', () => {
|
||||
const source = ref(0);
|
||||
const target1 = ref(0);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { watch, type Ref, type WatchOptions, type WatchSource } from 'vue';
|
||||
import { watch } from 'vue';
|
||||
import type { Ref, WatchOptions, WatchSource } from 'vue';
|
||||
import { isArray } from '@robonen/stdlib';
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ export function useSyncRefs<T = unknown>(
|
||||
|
||||
return watch(
|
||||
source,
|
||||
(value) => targets.forEach((target) => target.value = value),
|
||||
(value) => targets.forEach((target) => { target.value = value; }),
|
||||
{ flush, deep, immediate },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, it, vi, expect } from 'vitest';
|
||||
import { ref, reactive } from 'vue';
|
||||
import { useAppSharedState } from '.';
|
||||
|
||||
describe('useAppSharedState', () => {
|
||||
describe(useAppSharedState, () => {
|
||||
it('initialize state only once', () => {
|
||||
const stateFactory = (initValue?: number) => {
|
||||
const count = ref(initValue ?? 0);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { isShallow, nextTick, ref } from 'vue';
|
||||
import { it, expect, describe, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { useAsyncState } from '.';
|
||||
|
||||
describe('useAsyncState', () => {
|
||||
describe(useAsyncState, () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
@@ -18,15 +18,15 @@ describe('useAsyncState', () => {
|
||||
);
|
||||
|
||||
expect(state.value).toBe('initial');
|
||||
expect(isReady.value).toBe(false);
|
||||
expect(isLoading.value).toBe(true);
|
||||
expect(isReady.value).toBeFalsy();
|
||||
expect(isLoading.value).toBeTruthy();
|
||||
expect(error.value).toBe(null);
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(state.value).toBe('data');
|
||||
expect(isReady.value).toBe(true);
|
||||
expect(isLoading.value).toBe(false);
|
||||
expect(isReady.value).toBeTruthy();
|
||||
expect(isLoading.value).toBeFalsy();
|
||||
expect(error.value).toBe(null);
|
||||
});
|
||||
|
||||
@@ -37,15 +37,15 @@ describe('useAsyncState', () => {
|
||||
);
|
||||
|
||||
expect(state.value).toBe('initial');
|
||||
expect(isReady.value).toBe(false);
|
||||
expect(isLoading.value).toBe(true);
|
||||
expect(isReady.value).toBeFalsy();
|
||||
expect(isLoading.value).toBeTruthy();
|
||||
expect(error.value).toBe(null);
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(state.value).toBe('data');
|
||||
expect(isReady.value).toBe(true);
|
||||
expect(isLoading.value).toBe(false);
|
||||
expect(isReady.value).toBeTruthy();
|
||||
expect(isLoading.value).toBeFalsy();
|
||||
expect(error.value).toBe(null);
|
||||
});
|
||||
|
||||
@@ -56,15 +56,15 @@ describe('useAsyncState', () => {
|
||||
);
|
||||
|
||||
expect(state.value).toBe('initial');
|
||||
expect(isReady.value).toBe(false);
|
||||
expect(isLoading.value).toBe(true);
|
||||
expect(isReady.value).toBeFalsy();
|
||||
expect(isLoading.value).toBeTruthy();
|
||||
expect(error.value).toBe(null);
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(state.value).toBe('initial');
|
||||
expect(isReady.value).toBe(false);
|
||||
expect(isLoading.value).toBe(false);
|
||||
expect(isReady.value).toBeFalsy();
|
||||
expect(isLoading.value).toBeFalsy();
|
||||
expect(error.value).toEqual(new Error('test-error'));
|
||||
});
|
||||
|
||||
@@ -131,14 +131,14 @@ describe('useAsyncState', () => {
|
||||
);
|
||||
|
||||
const promise = execute();
|
||||
expect(isLoading.value).toBe(true);
|
||||
expect(isLoading.value).toBeTruthy();
|
||||
|
||||
await vi.advanceTimersByTimeAsync(50);
|
||||
expect(isLoading.value).toBe(true);
|
||||
expect(isLoading.value).toBeTruthy();
|
||||
|
||||
await vi.advanceTimersByTimeAsync(50);
|
||||
await promise;
|
||||
expect(isLoading.value).toBe(false);
|
||||
expect(isLoading.value).toBeFalsy();
|
||||
});
|
||||
|
||||
it('is awaitable', async () => {
|
||||
@@ -160,15 +160,15 @@ describe('useAsyncState', () => {
|
||||
executeImmediately();
|
||||
|
||||
expect(state.value).toBe('initial');
|
||||
expect(isLoading.value).toBe(true);
|
||||
expect(isReady.value).toBe(false);
|
||||
expect(isLoading.value).toBeTruthy();
|
||||
expect(isReady.value).toBeFalsy();
|
||||
expect(error.value).toBe(null);
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(state.value).toBe('data');
|
||||
expect(isReady.value).toBe(true);
|
||||
expect(isLoading.value).toBe(false);
|
||||
expect(isReady.value).toBeTruthy();
|
||||
expect(isLoading.value).toBeFalsy();
|
||||
expect(error.value).toBe(null);
|
||||
});
|
||||
|
||||
@@ -193,7 +193,7 @@ describe('useAsyncState', () => {
|
||||
);
|
||||
|
||||
expect(state.value.a).toBe(1);
|
||||
expect(isShallow(state)).toBe(true);
|
||||
expect(isShallow(state)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('uses ref when shallow is false', async () => {
|
||||
@@ -204,6 +204,6 @@ describe('useAsyncState', () => {
|
||||
);
|
||||
|
||||
expect(state.value.a).toBe(1);
|
||||
expect(isShallow(state)).toBe(false);
|
||||
expect(isShallow(state)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ref, shallowRef, watch, type Ref, type ShallowRef, type UnwrapRef } from 'vue';
|
||||
import { ref, shallowRef, watch } from 'vue';
|
||||
import type { Ref, ShallowRef, UnwrapRef } from 'vue';
|
||||
import { isFunction, sleep } from '@robonen/stdlib';
|
||||
|
||||
export interface UseAsyncStateOptions<Shallow extends boolean, Data = any> {
|
||||
@@ -103,8 +104,12 @@ export function useAsyncState<Data, Params extends any[] = [], Shallow extends b
|
||||
watch(
|
||||
isLoading,
|
||||
(loading) => {
|
||||
if (loading === false)
|
||||
error.value ? reject(error.value) : resolve(shell);
|
||||
if (loading === false) {
|
||||
if (error.value)
|
||||
reject(error.value);
|
||||
else
|
||||
resolve(shell);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
@@ -117,6 +122,7 @@ export function useAsyncState<Data, Params extends any[] = [], Shallow extends b
|
||||
|
||||
return {
|
||||
...shell,
|
||||
// eslint-disable-next-line unicorn/no-thenable
|
||||
then(onFulfilled, onRejected) {
|
||||
return waitResolve().then(onFulfilled, onRejected);
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@ function testFactory<Data>(
|
||||
|
||||
// TODO: maybe replace template with passing mock functions to setup
|
||||
|
||||
describe('useContextFactory', () => {
|
||||
describe(useContextFactory, () => {
|
||||
it('provide and inject context correctly', () => {
|
||||
const { Parent } = testFactory('test', useContextFactory('TestContext'));
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { inject as vueInject, provide as vueProvide, type InjectionKey, type App } from 'vue';
|
||||
import { inject as vueInject, provide as vueProvide } from 'vue';
|
||||
import type { InjectionKey, App } from 'vue';
|
||||
import { VueToolsError } from '@/utils';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import { it, expect, describe } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
import { useCounter } from '.';
|
||||
|
||||
describe('useCounter', () => {
|
||||
describe(useCounter, () => {
|
||||
it('initialize count with the provided initial value', () => {
|
||||
const { count } = useCounter(5);
|
||||
expect(count.value).toBe(5);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ref, toValue, type MaybeRefOrGetter, type Ref } from 'vue';
|
||||
import { ref, toValue } from 'vue';
|
||||
import type { MaybeRefOrGetter, Ref } from 'vue';
|
||||
import { clamp } from '@robonen/stdlib';
|
||||
|
||||
export interface UseCounterOptions {
|
||||
@@ -46,14 +47,17 @@ export function useCounter(
|
||||
max = Number.MAX_SAFE_INTEGER,
|
||||
} = options;
|
||||
|
||||
const increment = (delta = 1) =>
|
||||
const increment = (delta = 1) => {
|
||||
count.value = clamp(count.value + delta, min, max);
|
||||
};
|
||||
|
||||
const decrement = (delta = 1) =>
|
||||
const decrement = (delta = 1) => {
|
||||
count.value = clamp(count.value - delta, min, max);
|
||||
};
|
||||
|
||||
const set = (value: number) =>
|
||||
const set = (value: number) => {
|
||||
count.value = clamp(value, min, max);
|
||||
};
|
||||
|
||||
const get = () => count.value;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useContextFactory } from '../useContextFactory';
|
||||
import type { App, InjectionKey } from 'vue';
|
||||
import type { App } from 'vue';
|
||||
|
||||
export interface useInjectionStoreOptions<Return> {
|
||||
injectionName?: string;
|
||||
|
||||
@@ -2,56 +2,56 @@ import { it, expect, describe } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
import { useToggle } from '.';
|
||||
|
||||
describe('useToggle', () => {
|
||||
describe(useToggle, () => {
|
||||
it('initialize with false by default', () => {
|
||||
const { value } = useToggle();
|
||||
expect(value.value).toBe(false);
|
||||
expect(value.value).toBeFalsy();
|
||||
});
|
||||
|
||||
it('initialize with the provided initial value', () => {
|
||||
const { value } = useToggle(true);
|
||||
expect(value.value).toBe(true);
|
||||
expect(value.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('initialize with the provided initial value from a ref', () => {
|
||||
const { value } = useToggle(ref(true));
|
||||
expect(value.value).toBe(true);
|
||||
expect(value.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('toggle from false to true', () => {
|
||||
const { value, toggle } = useToggle(false);
|
||||
toggle();
|
||||
expect(value.value).toBe(true);
|
||||
expect(value.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('toggle from true to false', () => {
|
||||
const { value, toggle } = useToggle(true);
|
||||
toggle();
|
||||
expect(value.value).toBe(false);
|
||||
expect(value.value).toBeFalsy();
|
||||
});
|
||||
|
||||
it('toggle multiple times', () => {
|
||||
const { value, toggle } = useToggle(false);
|
||||
toggle();
|
||||
expect(value.value).toBe(true);
|
||||
expect(value.value).toBeTruthy();
|
||||
toggle();
|
||||
expect(value.value).toBe(false);
|
||||
expect(value.value).toBeFalsy();
|
||||
toggle();
|
||||
expect(value.value).toBe(true);
|
||||
expect(value.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('toggle returns the new value', () => {
|
||||
const { toggle } = useToggle(false);
|
||||
expect(toggle()).toBe(true);
|
||||
expect(toggle()).toBe(false);
|
||||
expect(toggle()).toBeTruthy();
|
||||
expect(toggle()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('set a specific value via toggle', () => {
|
||||
const { value, toggle } = useToggle(false);
|
||||
toggle(true);
|
||||
expect(value.value).toBe(true);
|
||||
expect(value.value).toBeTruthy();
|
||||
toggle(true);
|
||||
expect(value.value).toBe(true);
|
||||
expect(value.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('use custom truthy and falsy values', () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ref, toValue, type MaybeRefOrGetter, type MaybeRef, type Ref } from 'vue';
|
||||
import { ref, toValue } from 'vue';
|
||||
import type { MaybeRefOrGetter, MaybeRef, Ref } from 'vue';
|
||||
|
||||
export interface UseToggleOptions<Truthy, Falsy> {
|
||||
truthyValue?: MaybeRefOrGetter<Truthy>,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { nextTick } from 'vue';
|
||||
import { useLocalStorage } from '.';
|
||||
|
||||
describe('useLocalStorage', () => {
|
||||
describe(useLocalStorage, () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { MaybeRefOrGetter, Ref } from 'vue';
|
||||
import { defaultWindow } from '@/types';
|
||||
import { VueToolsError } from '@/utils/error';
|
||||
import { useStorage, type UseStorageOptions } from '../useStorage';
|
||||
import { useStorage } from '../useStorage';
|
||||
import type { UseStorageOptions } from '../useStorage';
|
||||
|
||||
/**
|
||||
* @name useLocalStorage
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { nextTick } from 'vue';
|
||||
import { useSessionStorage } from '.';
|
||||
|
||||
describe('useSessionStorage', () => {
|
||||
describe(useSessionStorage, () => {
|
||||
beforeEach(() => {
|
||||
sessionStorage.clear();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { MaybeRefOrGetter, Ref } from 'vue';
|
||||
import { defaultWindow } from '@/types';
|
||||
import { VueToolsError } from '@/utils/error';
|
||||
import { useStorage, type UseStorageOptions } from '../useStorage';
|
||||
import { useStorage } from '../useStorage';
|
||||
import type { UseStorageOptions } from '../useStorage';
|
||||
|
||||
/**
|
||||
* @name useSessionStorage
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { nextTick } from 'vue';
|
||||
import { useStorage, StorageSerializers, type StorageLike } from '.';
|
||||
import { useStorage, StorageSerializers } from '.';
|
||||
import type { StorageLike } from '.';
|
||||
|
||||
function createMockStorage(): StorageLike & { store: Map<string, string> } {
|
||||
const store = new Map<string, string>();
|
||||
@@ -13,7 +14,7 @@ function createMockStorage(): StorageLike & { store: Map<string, string> } {
|
||||
};
|
||||
}
|
||||
|
||||
describe('useStorage', () => {
|
||||
describe(useStorage, () => {
|
||||
// --- Basic types ---
|
||||
|
||||
it('stores and reads a string', async () => {
|
||||
@@ -45,7 +46,7 @@ describe('useStorage', () => {
|
||||
const storage = createMockStorage();
|
||||
const state = useStorage<boolean>('test-bool', true, storage);
|
||||
|
||||
expect(state.value).toBe(true);
|
||||
expect(state.value).toBeTruthy();
|
||||
|
||||
state.value = false;
|
||||
await nextTick();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ref, shallowRef, watch, toValue, type Ref, type MaybeRefOrGetter } from 'vue';
|
||||
import { ref, shallowRef, watch, toValue } from 'vue';
|
||||
import type { Ref, MaybeRefOrGetter } from 'vue';
|
||||
import { isBoolean, isNumber, isString, isObject, isMap, isSet, isDate } from '@robonen/stdlib';
|
||||
import type { ConfigurableFlush } from '@/types';
|
||||
|
||||
@@ -145,7 +146,7 @@ export function useStorage<T>(
|
||||
flush = 'pre',
|
||||
writeDefaults = true,
|
||||
mergeDefaults = false,
|
||||
onError = console.error,
|
||||
onError = console.error, // eslint-disable-line no-console
|
||||
} = options;
|
||||
|
||||
const defaults = toValue(initialValue);
|
||||
@@ -156,8 +157,8 @@ export function useStorage<T>(
|
||||
function read(): T {
|
||||
const raw = storage.getItem(key);
|
||||
|
||||
if (raw == null) {
|
||||
if (writeDefaults && defaults != null) {
|
||||
if (raw === undefined || raw === null) {
|
||||
if (writeDefaults && defaults !== undefined && defaults !== null) {
|
||||
try {
|
||||
storage.setItem(key, serializer.write(defaults));
|
||||
} catch (e) {
|
||||
@@ -188,7 +189,7 @@ export function useStorage<T>(
|
||||
try {
|
||||
const oldValue = storage.getItem(key);
|
||||
|
||||
if (value == null) {
|
||||
if (value === undefined || value === null) {
|
||||
storage.removeItem(key);
|
||||
} else {
|
||||
const serialized = serializer.write(value);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { nextTick } from 'vue';
|
||||
import { useStorageAsync, type StorageLikeAsync } from '.';
|
||||
import { useStorageAsync } from '.';
|
||||
import type { StorageLikeAsync } from '.';
|
||||
|
||||
function createMockAsyncStorage(): StorageLikeAsync & { store: Map<string, string> } {
|
||||
const store = new Map<string, string>();
|
||||
@@ -24,7 +25,7 @@ function createDelayedAsyncStorage(delay: number): StorageLikeAsync & { store: M
|
||||
};
|
||||
}
|
||||
|
||||
describe('useStorageAsync', () => {
|
||||
describe(useStorageAsync, () => {
|
||||
// --- Basic read/write ---
|
||||
|
||||
it('returns default value before storage is ready', () => {
|
||||
@@ -32,7 +33,7 @@ describe('useStorageAsync', () => {
|
||||
const { state, isReady } = useStorageAsync('key', 'default', storage);
|
||||
|
||||
expect(state.value).toBe('default');
|
||||
expect(isReady.value).toBe(false);
|
||||
expect(isReady.value).toBeFalsy();
|
||||
});
|
||||
|
||||
it('reads existing value from async storage', async () => {
|
||||
@@ -42,7 +43,7 @@ describe('useStorageAsync', () => {
|
||||
const { state, isReady } = await useStorageAsync('key', 'default', storage);
|
||||
|
||||
expect(state.value).toBe('stored');
|
||||
expect(isReady.value).toBe(true);
|
||||
expect(isReady.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('writes value to async storage on change', async () => {
|
||||
@@ -81,7 +82,7 @@ describe('useStorageAsync', () => {
|
||||
|
||||
const { state } = await useStorageAsync('flag', false, storage);
|
||||
|
||||
expect(state.value).toBe(true);
|
||||
expect(state.value).toBeTruthy();
|
||||
|
||||
state.value = false;
|
||||
await nextTick();
|
||||
@@ -108,7 +109,7 @@ describe('useStorageAsync', () => {
|
||||
const { state, isReady } = await useStorageAsync('delayed', 'default', storage);
|
||||
|
||||
expect(state.value).toBe('loaded');
|
||||
expect(isReady.value).toBe(true);
|
||||
expect(isReady.value).toBeTruthy();
|
||||
});
|
||||
|
||||
// --- onReady callback ---
|
||||
@@ -201,7 +202,7 @@ describe('useStorageAsync', () => {
|
||||
await nextTick();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(storage.store.has('nullable')).toBe(false);
|
||||
expect(storage.store.has('nullable')).toBeFalsy();
|
||||
});
|
||||
|
||||
// --- Error handling ---
|
||||
@@ -321,7 +322,7 @@ describe('useStorageAsync', () => {
|
||||
|
||||
await useStorageAsync('new-key', 'default-val', storage, { writeDefaults: false });
|
||||
|
||||
expect(storage.store.has('new-key')).toBe(false);
|
||||
expect(storage.store.has('new-key')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not overwrite existing value with defaults', async () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ref, shallowRef, watch, toValue, type Ref, type ShallowRef, type MaybeRefOrGetter, type UnwrapRef } from 'vue';
|
||||
import { ref, shallowRef, watch, toValue } from 'vue';
|
||||
import type { Ref, ShallowRef, MaybeRefOrGetter, UnwrapRef } from 'vue';
|
||||
import type { ConfigurableFlush } from '@/types';
|
||||
import { tryOnScopeDispose } from '@/composables/lifecycle/tryOnScopeDispose';
|
||||
import { guessSerializer, shallowMerge } from '../useStorage';
|
||||
@@ -100,7 +101,7 @@ export function useStorageAsync<T, Shallow extends boolean = true>(
|
||||
writeDefaults = true,
|
||||
mergeDefaults = false,
|
||||
onReady,
|
||||
onError = console.error,
|
||||
onError = console.error, // eslint-disable-line no-console
|
||||
} = options;
|
||||
|
||||
const defaults = toValue(initialValue);
|
||||
@@ -113,8 +114,8 @@ export function useStorageAsync<T, Shallow extends boolean = true>(
|
||||
try {
|
||||
const raw = await storage.getItem(key);
|
||||
|
||||
if (raw == null) {
|
||||
if (writeDefaults && defaults != null) {
|
||||
if (raw === undefined || raw === null) {
|
||||
if (writeDefaults && defaults !== undefined && defaults !== null) {
|
||||
try {
|
||||
await storage.setItem(key, await serializer.write(defaults));
|
||||
} catch (e) {
|
||||
@@ -142,7 +143,7 @@ export function useStorageAsync<T, Shallow extends boolean = true>(
|
||||
|
||||
async function write(value: T) {
|
||||
try {
|
||||
if (value == null) {
|
||||
if (value === undefined || value === null) {
|
||||
await storage.removeItem(key);
|
||||
} else {
|
||||
const raw = await serializer.write(value);
|
||||
@@ -179,6 +180,7 @@ export function useStorageAsync<T, Shallow extends boolean = true>(
|
||||
|
||||
return {
|
||||
...shell,
|
||||
// eslint-disable-next-line unicorn/no-thenable
|
||||
then(onFulfilled, onRejected) {
|
||||
return readyPromise.then(onFulfilled, onRejected);
|
||||
},
|
||||
|
||||
@@ -2,14 +2,14 @@ import { describe, it, expect, vi } from 'vitest';
|
||||
import { nextTick, ref } from 'vue';
|
||||
import { useOffsetPagination } from '.';
|
||||
|
||||
describe('useOffsetPagination', () => {
|
||||
describe(useOffsetPagination, () => {
|
||||
it('initialize with default values without options', () => {
|
||||
const { currentPage, currentPageSize, totalPages, isFirstPage } = useOffsetPagination({});
|
||||
|
||||
expect(currentPage.value).toBe(1);
|
||||
expect(currentPageSize.value).toBe(10);
|
||||
expect(totalPages.value).toBe(Infinity);
|
||||
expect(isFirstPage.value).toBe(true);
|
||||
expect(isFirstPage.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('calculate total pages correctly', () => {
|
||||
@@ -51,14 +51,14 @@ describe('useOffsetPagination', () => {
|
||||
const { currentPage, isFirstPage, isLastPage } = useOffsetPagination({ total: 20, pageSize: 10 });
|
||||
|
||||
expect(currentPage.value).toBe(1);
|
||||
expect(isFirstPage.value).toBe(true);
|
||||
expect(isLastPage.value).toBe(false);
|
||||
expect(isFirstPage.value).toBeTruthy();
|
||||
expect(isLastPage.value).toBeFalsy();
|
||||
|
||||
currentPage.value = 2;
|
||||
|
||||
expect(currentPage.value).toBe(2);
|
||||
expect(isFirstPage.value).toBe(false);
|
||||
expect(isLastPage.value).toBe(true);
|
||||
expect(isFirstPage.value).toBeFalsy();
|
||||
expect(isLastPage.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('call onPageChange callback', async () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
import { computed, reactive, toValue, watch, type ComputedRef, type MaybeRef, type MaybeRefOrGetter, type UnwrapNestedRefs, type WritableComputedRef } from 'vue';
|
||||
import { computed, reactive, toValue, watch } from 'vue';
|
||||
import type { ComputedRef, MaybeRef, MaybeRefOrGetter, UnwrapNestedRefs, WritableComputedRef } from 'vue';
|
||||
import { useClamp } from '@/composables/math/useClamp';
|
||||
|
||||
// TODO: sync returned refs with passed refs
|
||||
@@ -89,7 +90,7 @@ export function useOffsetPagination(options: UseOffsetPaginationOptions): UseOff
|
||||
|
||||
const next = () => currentPage.value++;
|
||||
const previous = () => currentPage.value--;
|
||||
const select = (page: number) => currentPage.value = page;
|
||||
const select = (page: number) => { currentPage.value = page; };
|
||||
|
||||
const returnValue = {
|
||||
currentPage,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isClient } from '@robonen/platform/multi';
|
||||
|
||||
export const defaultWindow = /* #__PURE__ */ isClient ? window : undefined
|
||||
export const defaultWindow = /* #__PURE__ */ isClient ? globalThis as Window & typeof globalThis : undefined
|
||||
|
||||
export interface ConfigurableWindow {
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getCurrentInstance, type ComponentInternalInstance } from 'vue';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
|
||||
/**
|
||||
* @name getLifeCycleTarger
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import { resolve } from 'path';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user