1
0
mirror of https://github.com/robonen/tools.git synced 2026-03-20 19:04:46 +00:00

feat(monorepo): migrate vue packages and apply oxlint refactors

This commit is contained in:
2026-03-07 18:07:22 +07:00
parent abd6605db3
commit 41d5e18f6b
286 changed files with 10295 additions and 5028 deletions

View File

@@ -0,0 +1,4 @@
export * from './tryOnBeforeMount';
export * from './tryOnMounted';
export * from './tryOnScopeDispose';
export * from './useMounted';

View File

@@ -0,0 +1,46 @@
import { onBeforeMount, nextTick } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import { getLifeCycleTarger } from '@/utils';
import type { VoidFunction } from '@robonen/stdlib';
// TODO: test
export interface TryOnBeforeMountOptions {
sync?: boolean;
target?: ComponentInternalInstance;
}
/**
* @name tryOnBeforeMount
* @category Lifecycle
* @description Call onBeforeMount if it's inside a component lifecycle hook, otherwise just calls it
*
* @param {VoidFunction} fn - The function to run on before mount.
* @param {TryOnBeforeMountOptions} options - The options for the function.
* @param {boolean} [options.sync=true] - If true, the function will run synchronously, otherwise it will run asynchronously.
* @param {ComponentInternalInstance} [options.target] - The target component instance to run the function on.
* @returns {void}
*
* @example
* tryOnBeforeMount(() => console.log('Before mount'));
*
* @example
* tryOnBeforeMount(() => console.log('Before mount async'), { sync: false });
*
* @since 0.0.1
*/
export function tryOnBeforeMount(fn: VoidFunction, options: TryOnBeforeMountOptions = {}) {
const {
sync = true,
target,
} = options;
const instance = getLifeCycleTarger(target);
if (instance)
onBeforeMount(fn, instance);
else if (sync)
fn();
else
nextTick(fn);
}

View File

@@ -0,0 +1,60 @@
import { describe, it, vi, expect } from 'vitest';
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';
const ComponentStub = defineComponent({
props: {
callback: {
type: Function as PropType<VoidFunction>,
},
},
setup(props) {
if (props.callback) {
tryOnMounted(props.callback);
}
},
template: `<div></div>`,
});
describe(tryOnMounted, () => {
it('run the callback when mounted', () => {
const callback = vi.fn();
mount(ComponentStub, {
props: { callback },
});
expect(callback).toHaveBeenCalled();
});
it('run the callback outside of a component lifecycle', () => {
const callback = vi.fn();
tryOnMounted(callback);
expect(callback).toHaveBeenCalled();
});
it('run the callback asynchronously', async () => {
const callback = vi.fn();
tryOnMounted(callback, { sync: false });
expect(callback).not.toHaveBeenCalled();
await nextTick();
expect(callback).toHaveBeenCalled();
});
it.skip('run the callback with a specific target', () => {
const callback = vi.fn();
const component = mount(ComponentStub);
tryOnMounted(callback, { target: component.vm.$ });
expect(callback).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,46 @@
import { onMounted, nextTick } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import { getLifeCycleTarger } from '@/utils';
import type { VoidFunction } from '@robonen/stdlib';
// TODO: tests
export interface TryOnMountedOptions {
sync?: boolean;
target?: ComponentInternalInstance;
}
/**
* @name tryOnMounted
* @category Lifecycle
* @description Call onMounted if it's inside a component lifecycle hook, otherwise just calls it
*
* @param {VoidFunction} fn The function to call
* @param {TryOnMountedOptions} options The options to use
* @param {boolean} [options.sync=true] If the function should be called synchronously
* @param {ComponentInternalInstance} [options.target] The target instance to use
* @returns {void}
*
* @example
* tryOnMounted(() => console.log('Mounted!'));
*
* @example
* tryOnMounted(() => console.log('Mounted!'), { sync: false });
*
* @since 0.0.1
*/
export function tryOnMounted(fn: VoidFunction, options: TryOnMountedOptions = {}) {
const {
sync = true,
target,
} = options;
const instance = getLifeCycleTarger(target);
if (instance)
onMounted(fn, instance);
else if (sync)
fn();
else
nextTick(fn);
}

View File

@@ -0,0 +1,59 @@
import { describe, expect, it, vi } from 'vitest';
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';
const ComponentStub = defineComponent({
props: {
callback: {
type: Function as PropType<VoidFunction>,
required: true,
},
},
setup(props) {
tryOnScopeDispose(props.callback);
},
template: '<div></div>',
});
describe(tryOnScopeDispose, () => {
it('returns false when the scope is not active', () => {
const callback = vi.fn();
const detectedScope = tryOnScopeDispose(callback);
expect(detectedScope).toBeFalsy();
expect(callback).not.toHaveBeenCalled();
});
it('run the callback when the scope is disposed', () => {
const callback = vi.fn();
const scope = effectScope();
let detectedScope: boolean | undefined;
scope.run(() => {
detectedScope = tryOnScopeDispose(callback);
});
expect(detectedScope).toBeTruthy();
expect(callback).not.toHaveBeenCalled();
scope.stop();
expect(callback).toHaveBeenCalled();
});
it('run callback when the component is unmounted', () => {
const callback = vi.fn();
const component = mount(ComponentStub, {
props: { callback },
});
expect(callback).not.toHaveBeenCalled();
component.unmount();
expect(callback).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,24 @@
import type { VoidFunction } from '@robonen/stdlib';
import { getCurrentScope, onScopeDispose } from 'vue';
/**
* @name tryOnScopeDispose
* @category Lifecycle
* @description A composable that will run a callback when the scope is disposed or do nothing if the scope isn't available.
*
* @param {VoidFunction} callback - The callback to run when the scope is disposed.
* @returns {boolean} - Returns true if the callback was run, otherwise false.
*
* @example
* tryOnScopeDispose(() => console.log('Scope disposed'));
*
* @since 0.0.1
*/
export function tryOnScopeDispose(callback: VoidFunction) {
if (getCurrentScope()) {
onScopeDispose(callback);
return true;
}
return false;
}

View File

@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest';
import { defineComponent, nextTick, ref } from 'vue';
import { mount } from '@vue/test-utils';
import { useMounted } from '.';
const ComponentStub = defineComponent({
setup() {
const isMounted = useMounted();
return { isMounted };
},
template: `<div>{{ isMounted }}</div>`,
});
describe(useMounted, () => {
it('return the mounted state of the component', async () => {
const component = mount(ComponentStub);
// Initial render
expect(component.text()).toBe('false');
await nextTick();
// Will trigger a render
expect(component.text()).toBe('true');
});
});

View File

@@ -0,0 +1,30 @@
import { onMounted, readonly, ref } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import { getLifeCycleTarger } from '@/utils';
/**
* @name useMounted
* @category Lifecycle
* @description Returns a ref that tracks the mounted state of the component (doesn't track the unmounted state)
*
* @param {ComponentInternalInstance} [instance] The component instance to track the mounted state for
* @returns {Readonly<Ref<boolean>>} The mounted state of the component
*
* @example
* const isMounted = useMounted();
*
* @example
* const isMounted = useMounted(getCurrentInstance());
*
* @since 0.0.1
*/
export function useMounted(instance?: ComponentInternalInstance) {
const isMounted = ref(false);
const targetInstance = getLifeCycleTarger(instance);
onMounted(() => {
isMounted.value = true;
}, targetInstance);
return readonly(isMounted);
}