1
0
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:
2026-02-14 22:49:47 +07:00
parent 2a5412c3b8
commit 49b9f2aa79
98 changed files with 1236 additions and 201 deletions

View File

@@ -1 +1,32 @@
# @robonen/stdlib
# @robonen/stdlib
Standard library of platform-independent utilities for TypeScript.
## Install
```bash
pnpm install @robonen/stdlib
```
## Modules
| Module | Utilities |
| --------------- | --------------------------------------------------------------- |
| **arrays** | `cluster`, `first`, `last`, `sum`, `unique` |
| **async** | `sleep`, `tryIt` |
| **bits** | `flags` |
| **collections** | `get` |
| **math** | `clamp`, `lerp`, `remap` + BigInt variants |
| **objects** | `omit`, `pick` |
| **patterns** | `pubsub` |
| **structs** | `stack` |
| **sync** | `mutex` |
| **text** | `levenshteinDistance`, `trigramDistance` |
| **types** | JS & TS type utilities |
| **utils** | `timestamp`, `noop` |
## Usage
```ts
import { first, sleep, clamp } from '@robonen/stdlib';
```

View File

@@ -0,0 +1,4 @@
import { defineConfig } from 'oxlint';
import { compose, base, typescript, imports } from '@robonen/oxlint';
export default defineConfig(compose(base, typescript, imports));

View File

@@ -34,12 +34,15 @@
}
},
"scripts": {
"lint": "oxlint -c oxlint.config.ts",
"test": "vitest run",
"dev": "vitest dev",
"build": "tsdown"
},
"devDependencies": {
"@robonen/oxlint": "workspace:*",
"@robonen/tsconfig": "workspace:*",
"oxlint": "catalog:",
"tsdown": "catalog:"
}
}

View File

@@ -1,3 +1,3 @@
export type AsyncPoolOptions = {
export interface AsyncPoolOptions {
concurrency?: number;
}

View File

@@ -1,3 +1,4 @@
// eslint-disable
export interface RetryOptions {
times?: number;
delay?: number;

View File

@@ -1,4 +1,4 @@
export interface BitVector {
export interface BitVectorLike {
getBit(index: number): boolean;
setBit(index: number): void;
clearBit(index: number): void;
@@ -12,7 +12,7 @@ export interface BitVector {
*
* @since 0.0.3
*/
export class BitVector extends Uint8Array implements BitVector {
export class BitVector extends Uint8Array implements BitVectorLike {
constructor(size: number) {
super(Math.ceil(size / 8));
}

View File

@@ -1,4 +1,4 @@
import { type Collection, type Path } from '../../types';
import type { Collection, Path } from '../../types';
export type ExtractFromObject<O extends Record<PropertyKey, unknown>, K> =
K extends keyof O
@@ -9,7 +9,7 @@ export type ExtractFromObject<O extends Record<PropertyKey, unknown>, K> =
export type ExtractFromArray<A extends readonly any[], K> =
any[] extends A
? A extends readonly (infer T)[]
? A extends ReadonlyArray<infer T>
? T | undefined
: undefined
: K extends keyof A

View File

@@ -46,13 +46,13 @@ describe('clamp', () => {
it('handle NaN and Infinity', () => {
// value is NaN
expect(clamp(NaN, 0, 100)).toBe(NaN);
expect(clamp(Number.NaN, 0, 100)).toBe(Number.NaN);
// min is NaN
expect(clamp(50, NaN, 100)).toBe(NaN);
expect(clamp(50, Number.NaN, 100)).toBe(Number.NaN);
// max is NaN
expect(clamp(50, 0, NaN)).toBe(NaN);
expect(clamp(50, 0, Number.NaN)).toBe(Number.NaN);
// value is Infinity
expect(clamp(Infinity, 0, 100)).toBe(100);

View File

@@ -1,4 +1,5 @@
import { isArray, type Arrayable } from '../../types';
import { isArray } from '../../types';
import type { Arrayable } from '../../types';
/**
* @name omit

View File

@@ -1,4 +1,5 @@
import { isArray, type Arrayable } from '../../types';
import { isArray } from '../../types';
import type { Arrayable } from '../../types';
/**
* @name pick

View File

@@ -1,9 +1,9 @@
import { last } from '../../arrays';
import { isArray } from '../../types';
export type StackOptions = {
export interface StackOptions {
maxSize?: number;
};
}
/**
* @name Stack

View File

@@ -1,5 +1,3 @@
import type { MaybePromise } from "../../types";
/**
* @name SyncMutex
* @category Utils

View File

@@ -1,5 +1,6 @@
import { get } from '../../collections';
import { isFunction, type Path, type PathToType, type Stringable, type Trim, type UnionToIntersection } from '../../types';
import { isFunction } from '../../types';
import type { Collection, Path, PathToType, Stringable, Trim, UnionToIntersection } from '../../types';
/**
* Type of a value that will be used to replace a placeholder in a template.
@@ -55,7 +56,7 @@ export type GenerateTypes<T extends string, Target = string> = UnionToIntersecti
export function templateObject<
T extends string,
A extends GenerateTypes<ExtractPlaceholders<T>, TemplateValue>
A extends GenerateTypes<ExtractPlaceholders<T>, TemplateValue> & Collection
>(template: T, args: A, fallback?: TemplateFallback) {
return template.replace(TEMPLATE_PLACEHOLDER, (_, key) => {
const value = get(args, key)?.toString();

View File

@@ -11,7 +11,7 @@ export type Trigrams = Map<string, number>;
* @since 0.0.1
*/
export function trigramProfile(text: string): Trigrams {
text = '\n\n' + text + '\n\n';
text = `\n\n${text}\n\n`;
const trigrams = new Map<string, number>();

View File

@@ -19,7 +19,7 @@ describe('casts', () => {
expect(toString(null)).toBe('[object Null]');
expect(toString(/abc/)).toBe('[object RegExp]');
expect(toString(new Date())).toBe('[object Date]');
expect(toString(new Error())).toBe('[object Error]');
expect(toString(new Error('test'))).toBe('[object Error]');
expect(toString(new Promise(() => {}))).toBe('[object Promise]');
expect(toString(new Map())).toBe('[object Map]');
expect(toString(new Set())).toBe('[object Set]');

View File

@@ -77,7 +77,7 @@ describe('complex', () => {
describe('isError', () => {
it('true if the value is an error', () => {
expect(isError(new Error())).toBe(true);
expect(isError(new Error('test'))).toBe(true);
});
it('false if the value is not an error', () => {

View File

@@ -1,4 +1,4 @@
import { toString } from '.';
import { toString } from './casts';
/**
* @name isFunction

View File

@@ -1,4 +1,4 @@
import { toString } from '.';
import { toString } from './casts';
/**
* @name isObject

View File

@@ -35,35 +35,35 @@ describe('collections', () => {
describe('PathToType', () => {
it('convert simple object path', () => {
type actual = PathToType<['user', 'name']>;
type expected = { user: { name: unknown } };
interface expected { user: { name: unknown } }
expectTypeOf<actual>().toEqualTypeOf<expected>();
});
it('convert simple array path', () => {
type actual = PathToType<['user', '0']>;
type expected = { user: unknown[] };
interface expected { user: unknown[] }
expectTypeOf<actual>().toEqualTypeOf<expected>();
});
it('convert complex object path', () => {
type actual = PathToType<['user', 'addresses', '0', 'street']>;
type expected = { user: { addresses: { street: unknown }[] } };
interface expected { user: { addresses: Array<{ street: unknown }> } }
expectTypeOf<actual>().toEqualTypeOf<expected>();
});
it('convert double dot path', () => {
type actual = PathToType<['user', '', 'name']>;
type expected = { user: { '': { name: unknown } } };
interface expected { user: { '': { name: unknown } } }
expectTypeOf<actual>().toEqualTypeOf<expected>();
});
it('convert to custom target', () => {
type actual = PathToType<['user', 'name'], string>;
type expected = { user: { name: string } };
interface expected { user: { name: string } }
expectTypeOf<actual>().toEqualTypeOf<expected>();
});

View File

@@ -20,7 +20,7 @@ export type PathToType<T extends string[], Target = unknown> =
T extends [infer Head, ...infer Rest]
? Head extends `${number}`
? Rest extends string[]
? PathToType<Rest, Target>[]
? Array<PathToType<Rest, Target>>
: never
: Rest extends string[]
? { [K in Head & string]: PathToType<Rest, Target> }

View File

@@ -7,7 +7,7 @@ describe('string', () => {
expectTypeOf(Number(1)).toExtend<Stringable>();
expectTypeOf(String(1)).toExtend<Stringable>();
expectTypeOf(Symbol()).toExtend<Stringable>();
expectTypeOf(new Array(1)).toExtend<Stringable>();
expectTypeOf([1]).toExtend<Stringable>();
expectTypeOf(new Object()).toExtend<Stringable>();
expectTypeOf(new Date()).toExtend<Stringable>();
});