mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
chore: workspace improvements (#282)
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { animationFrame } from './animation-frame';
|
||||
import { animationFrame } from '../animation-frame';
|
||||
|
||||
describe('animationFrame', () => {
|
||||
beforeEach(() => {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { onEvent } from './event';
|
||||
import { onEvent } from '../event';
|
||||
|
||||
describe('onEvent', () => {
|
||||
it('returns a promise', () => {
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { idleCallback } from './idle-callback';
|
||||
import { idleCallback } from '../idle-callback';
|
||||
|
||||
describe('idleCallback', () => {
|
||||
beforeEach(() => {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { listen } from './listen';
|
||||
import { listen } from '../listen';
|
||||
|
||||
describe('listen', () => {
|
||||
it('adds an event listener', () => {
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { supportsAnimationFrame, supportsIdleCallback } from './supports';
|
||||
import { supportsAnimationFrame, supportsIdleCallback } from '../supports';
|
||||
|
||||
describe('supports', () => {
|
||||
describe('supportsAnimationFrame', () => {
|
||||
@@ -1,8 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"]
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"declarationDir": "../../types/dom"
|
||||
},
|
||||
"references": [{ "path": "../.." }],
|
||||
"include": ["./**/*.ts"]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isNumber, isObject, isString } from './predicate';
|
||||
import { isNumber, isObject, isString } from '../predicate';
|
||||
|
||||
export interface EventLike {
|
||||
type: string;
|
||||
@@ -13,10 +13,6 @@ export interface EventLike {
|
||||
*/
|
||||
export function isEventLike(value: unknown): value is EventLike {
|
||||
return (
|
||||
isObject(value)
|
||||
&& 'type' in value
|
||||
&& isString(value.type)
|
||||
&& 'timeStamp' in value
|
||||
&& isNumber(value.timeStamp)
|
||||
isObject(value) && 'type' in value && isString(value.type) && 'timeStamp' in value && isNumber(value.timeStamp)
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './disposer';
|
||||
export * from './event-like';
|
||||
export * from './predicate';
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { Disposer } from './disposer';
|
||||
import { Disposer } from '../disposer';
|
||||
|
||||
describe('disposer', () => {
|
||||
describe('constructor', () => {
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { isEventLike } from './event-like';
|
||||
import { isEventLike } from '../event-like';
|
||||
|
||||
describe('event-like', () => {
|
||||
describe('isEventLike', () => {
|
||||
@@ -0,0 +1,2 @@
|
||||
export { pick } from './pick';
|
||||
export { getSelectorKeys, type Selector } from './selector';
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Creates a new object with only the specified keys.
|
||||
*
|
||||
* @example
|
||||
* const obj = { a: 1, b: 2, c: 3 };
|
||||
* pick(obj, ['a', 'c']); // { a: 1, c: 3 }
|
||||
*/
|
||||
export function pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: readonly K[]): Pick<T, K> {
|
||||
const result = {} as Pick<T, K>;
|
||||
|
||||
for (const key of keys) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { isObject } from '../predicate/predicate';
|
||||
|
||||
/**
|
||||
* A function that selects a subset of state.
|
||||
*/
|
||||
export type Selector<State, Selected> = (state: State) => Selected;
|
||||
|
||||
/**
|
||||
* Extracts the state keys a selector depends on by running it once
|
||||
* and inspecting the result object's keys.
|
||||
*
|
||||
* Returns `null` if the selector returns a primitive or array
|
||||
* (keys cannot be determined).
|
||||
*
|
||||
* @example
|
||||
* const selector = (s: State) => ({ volume: s.volume, muted: s.muted });
|
||||
* getSelectorKeys(selector, state); // ['volume', 'muted']
|
||||
*
|
||||
* const primitiveSelector = (s: State) => s.volume;
|
||||
* getSelectorKeys(primitiveSelector, state); // null
|
||||
*/
|
||||
export function getSelectorKeys<State, Selected>(
|
||||
selector: Selector<State, Selected>,
|
||||
state: State,
|
||||
): (keyof State)[] | null {
|
||||
const result = selector(state);
|
||||
|
||||
if (!isObject(result) || Array.isArray(result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.keys(result) as (keyof State)[];
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { pick } from '../pick';
|
||||
|
||||
describe('pick', () => {
|
||||
it('picks specified keys from object', () => {
|
||||
const obj = { a: 1, b: 2, c: 3 };
|
||||
expect(pick(obj, ['a', 'c'])).toEqual({ a: 1, c: 3 });
|
||||
});
|
||||
|
||||
it('picks single key', () => {
|
||||
const obj = { a: 1, b: 2, c: 3 };
|
||||
expect(pick(obj, ['b'])).toEqual({ b: 2 });
|
||||
});
|
||||
|
||||
it('returns empty object for empty keys array', () => {
|
||||
const obj = { a: 1, b: 2 };
|
||||
expect(pick(obj, [])).toEqual({});
|
||||
});
|
||||
|
||||
it('ignores non-existent keys', () => {
|
||||
const obj = { a: 1, b: 2 } as Record<string, number>;
|
||||
expect(pick(obj, ['a', 'nonexistent'] as (keyof typeof obj)[])).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it('handles nested objects (shallow copy)', () => {
|
||||
const nested = { a: { x: 1 }, b: { y: 2 } };
|
||||
const result = pick(nested, ['a']);
|
||||
|
||||
expect(result).toEqual({ a: { x: 1 } });
|
||||
expect(result.a).toBe(nested.a); // Same reference (shallow)
|
||||
});
|
||||
|
||||
it('handles all keys', () => {
|
||||
const obj = { a: 1, b: 2 };
|
||||
expect(pick(obj, ['a', 'b'])).toEqual({ a: 1, b: 2 });
|
||||
});
|
||||
|
||||
it('preserves value types', () => {
|
||||
const obj = {
|
||||
str: 'hello',
|
||||
num: 42,
|
||||
bool: true,
|
||||
arr: [1, 2, 3],
|
||||
nil: null,
|
||||
undef: undefined,
|
||||
};
|
||||
|
||||
const result = pick(obj, ['str', 'num', 'bool', 'arr', 'nil', 'undef']);
|
||||
|
||||
expect(result).toEqual(obj);
|
||||
});
|
||||
|
||||
it('works with readonly keys array', () => {
|
||||
const obj = { a: 1, b: 2, c: 3 };
|
||||
const keys = ['a', 'c'] as const;
|
||||
|
||||
expect(pick(obj, keys)).toEqual({ a: 1, c: 3 });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { getSelectorKeys } from '../selector';
|
||||
|
||||
describe('getSelectorKeys', () => {
|
||||
interface State {
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
currentTime: number;
|
||||
}
|
||||
|
||||
const state: State = { volume: 1, muted: false, currentTime: 0 };
|
||||
|
||||
it('extracts keys from object selector result', () => {
|
||||
const selector = (s: State) => ({ volume: s.volume, muted: s.muted });
|
||||
expect(getSelectorKeys(selector, state)).toEqual(['volume', 'muted']);
|
||||
});
|
||||
|
||||
it('extracts single key', () => {
|
||||
const selector = (s: State) => ({ volume: s.volume });
|
||||
expect(getSelectorKeys(selector, state)).toEqual(['volume']);
|
||||
});
|
||||
|
||||
it('returns null for primitive selector result (number)', () => {
|
||||
const selector = (s: State) => s.volume;
|
||||
expect(getSelectorKeys(selector, state)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for primitive selector result (boolean)', () => {
|
||||
const selector = (s: State) => s.muted;
|
||||
expect(getSelectorKeys(selector, state)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for primitive selector result (string)', () => {
|
||||
const selector = () => 'hello';
|
||||
expect(getSelectorKeys(selector, state)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for array selector result', () => {
|
||||
const selector = (s: State) => [s.volume, s.muted];
|
||||
expect(getSelectorKeys(selector, state)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for null selector result', () => {
|
||||
const selector = () => null;
|
||||
expect(getSelectorKeys(selector, state)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for undefined selector result', () => {
|
||||
const selector = () => undefined;
|
||||
expect(getSelectorKeys(selector, state)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns empty array for empty object selector', () => {
|
||||
const selector = () => ({});
|
||||
expect(getSelectorKeys(selector, state)).toEqual([]);
|
||||
});
|
||||
|
||||
it('handles derived/computed properties in selector', () => {
|
||||
const selector = (s: State) => ({
|
||||
volumePercent: Math.round(s.volume * 100),
|
||||
isMuted: s.muted,
|
||||
});
|
||||
|
||||
// Returns the result object's keys, not the state keys accessed
|
||||
expect(getSelectorKeys(selector, state)).toEqual(['volumePercent', 'isMuted']);
|
||||
});
|
||||
|
||||
it('handles selector that accesses nested state', () => {
|
||||
interface NestedState {
|
||||
audio: { volume: number; muted: boolean };
|
||||
video: { quality: string };
|
||||
}
|
||||
|
||||
const nestedState: NestedState = {
|
||||
audio: { volume: 1, muted: false },
|
||||
video: { quality: 'hd' },
|
||||
};
|
||||
|
||||
const selector = (s: NestedState) => ({ audio: s.audio });
|
||||
expect(getSelectorKeys(selector, nestedState)).toEqual(['audio']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export * from './predicate';
|
||||
+1
-1
@@ -11,7 +11,7 @@ import {
|
||||
isPromise,
|
||||
isString,
|
||||
isUndefined,
|
||||
} from './predicate';
|
||||
} from '../predicate';
|
||||
|
||||
describe('predicate', () => {
|
||||
describe('isString', () => {
|
||||
@@ -0,0 +1 @@
|
||||
export * from './types';
|
||||
@@ -0,0 +1 @@
|
||||
export type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
|
||||
Reference in New Issue
Block a user