@oscarpalmer/atoms 0.172.3 → 0.173.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +18 -2
- package/dist/index.mjs +7 -4
- package/dist/queue.d.mts +18 -2
- package/dist/queue.mjs +7 -4
- package/package.json +3 -1
- package/plugin/get-string.js +66 -0
- package/plugin/helpers.js +118 -21
- package/plugin/index.js +49 -5
- package/plugin/rules.js +32 -0
- package/plugin/try-catch.js +47 -0
- package/src/queue.ts +30 -9
- package/plugin/array.js +0 -22
package/dist/index.d.mts
CHANGED
|
@@ -3739,8 +3739,24 @@ type QueueOptions = {
|
|
|
3739
3739
|
maximum?: number;
|
|
3740
3740
|
};
|
|
3741
3741
|
type Queued<Value> = {
|
|
3742
|
+
/**
|
|
3743
|
+
* ID of the queued promise _(can be used to remove it from the queue)_
|
|
3744
|
+
*/
|
|
3742
3745
|
readonly id: number;
|
|
3743
|
-
|
|
3746
|
+
/**
|
|
3747
|
+
* Queued promise
|
|
3748
|
+
*/
|
|
3749
|
+
readonly promise: Promise<QueuedResult<Value>>;
|
|
3750
|
+
};
|
|
3751
|
+
type QueuedResult<Value> = {
|
|
3752
|
+
/**
|
|
3753
|
+
* Has the queue finished processing all items?
|
|
3754
|
+
*/
|
|
3755
|
+
finished: boolean;
|
|
3756
|
+
/**
|
|
3757
|
+
* Result for the queued promise
|
|
3758
|
+
*/
|
|
3759
|
+
value: Value;
|
|
3744
3760
|
};
|
|
3745
3761
|
/**
|
|
3746
3762
|
* Create a queue for an asynchronous callback function
|
|
@@ -4388,4 +4404,4 @@ declare class SizedSet<Value = unknown> extends Set<Value> {
|
|
|
4388
4404
|
get(value: Value, update?: boolean): Value | undefined;
|
|
4389
4405
|
}
|
|
4390
4406
|
//#endregion
|
|
4391
|
-
export { AnyResult, ArrayComparisonSorter, ArrayKeySorter, ArrayOrPlainObject, ArrayPosition, ArrayValueSorter, Asserter, AsyncCancelableCallback, AttemptFlow, AttemptFlowPromise, type Beacon, type BeaconOptions, BuiltIns, CancelableCallback, CancelablePromise, type Color, Constructor, DiffOptions, DiffResult, DiffValue, EqualOptions, Err, EventPosition, ExtendedErr, ExtendedResult, Flow, FlowPromise, FulfilledPromise, GenericAsyncCallback, GenericCallback, type HSLAColor, type HSLColor, HasValue, Key, KeyedValue, type Logger, type Memoized, type MemoizedOptions, MergeOptions, Merger, NestedArray, NestedKeys, NestedPartial, NestedValue, NestedValues, NumericalKeys, NumericalValues, type Observable, type Observer, Ok, OnceAsyncCallback, OnceCallback, PROMISE_ABORT_EVENT, PROMISE_ABORT_OPTIONS, PROMISE_ERROR_NAME, PROMISE_MESSAGE_EXPECTATION_ATTEMPT, PROMISE_MESSAGE_EXPECTATION_RESULT, PROMISE_MESSAGE_EXPECTATION_TIMED, PROMISE_MESSAGE_TIMEOUT, PROMISE_STRATEGY_ALL, PROMISE_STRATEGY_DEFAULT, PROMISE_TYPE_FULFILLED, PROMISE_TYPE_REJECTED, PlainObject, Primitive, PromiseData, PromiseHandlers, PromiseOptions, PromiseParameters, PromiseStrategy, PromiseTimeoutError, PromisesItems, PromisesOptions, PromisesResult, PromisesUnwrapped, PromisesValue, PromisesValues, type Queue, QueueError, type QueueOptions, type Queued, type RGBAColor, type RGBColor, RejectedPromise, RequiredKeys, Result, ResultMatch, RetryError, RetryOptions, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, Simplify, SizedMap, SizedSet, Smushed, SortDirection, Sorter, type Subscription, TemplateOptions, type Time, ToString, TypedArray, Unsmushed, UnwrapValue, assert, attempt, attemptFlow, attemptPipe, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, endsWithArray, equal, error, exists, filter, find, flatten, floor, flow, fromQuery, toPromise as fromResult, toPromise, getArray, getArrayPosition, getColor, getError, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getTimedPromise, getUuid, getValue, groupBy, handleResult, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, includesArray, indexOf, indexOfArray, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, matchResult, max, median, memoize, merge, min, move, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, settlePromise, shuffle, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
|
|
4407
|
+
export { AnyResult, ArrayComparisonSorter, ArrayKeySorter, ArrayOrPlainObject, ArrayPosition, ArrayValueSorter, Asserter, AsyncCancelableCallback, AttemptFlow, AttemptFlowPromise, type Beacon, type BeaconOptions, BuiltIns, CancelableCallback, CancelablePromise, type Color, Constructor, DiffOptions, DiffResult, DiffValue, EqualOptions, Err, EventPosition, ExtendedErr, ExtendedResult, Flow, FlowPromise, FulfilledPromise, GenericAsyncCallback, GenericCallback, type HSLAColor, type HSLColor, HasValue, Key, KeyedValue, type Logger, type Memoized, type MemoizedOptions, MergeOptions, Merger, NestedArray, NestedKeys, NestedPartial, NestedValue, NestedValues, NumericalKeys, NumericalValues, type Observable, type Observer, Ok, OnceAsyncCallback, OnceCallback, PROMISE_ABORT_EVENT, PROMISE_ABORT_OPTIONS, PROMISE_ERROR_NAME, PROMISE_MESSAGE_EXPECTATION_ATTEMPT, PROMISE_MESSAGE_EXPECTATION_RESULT, PROMISE_MESSAGE_EXPECTATION_TIMED, PROMISE_MESSAGE_TIMEOUT, PROMISE_STRATEGY_ALL, PROMISE_STRATEGY_DEFAULT, PROMISE_TYPE_FULFILLED, PROMISE_TYPE_REJECTED, PlainObject, Primitive, PromiseData, PromiseHandlers, PromiseOptions, PromiseParameters, PromiseStrategy, PromiseTimeoutError, PromisesItems, PromisesOptions, PromisesResult, PromisesUnwrapped, PromisesValue, PromisesValues, type Queue, QueueError, type QueueOptions, type Queued, type QueuedResult, type RGBAColor, type RGBColor, RejectedPromise, RequiredKeys, Result, ResultMatch, RetryError, RetryOptions, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, Simplify, SizedMap, SizedSet, Smushed, SortDirection, Sorter, type Subscription, TemplateOptions, type Time, ToString, TypedArray, Unsmushed, UnwrapValue, assert, attempt, attemptFlow, attemptPipe, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, endsWithArray, equal, error, exists, filter, find, flatten, floor, flow, fromQuery, toPromise as fromResult, toPromise, getArray, getArrayPosition, getColor, getError, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getTimedPromise, getUuid, getValue, groupBy, handleResult, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, includesArray, indexOf, indexOfArray, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, matchResult, max, median, memoize, merge, min, move, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, settlePromise, shuffle, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
|
package/dist/index.mjs
CHANGED
|
@@ -4281,11 +4281,11 @@ var Queue = class {
|
|
|
4281
4281
|
if (this.#paused) {
|
|
4282
4282
|
const paused = item;
|
|
4283
4283
|
this.#handled.push(() => {
|
|
4284
|
-
handleResult$1(paused, error, result);
|
|
4284
|
+
handleResult$1(paused, error, result, this.#items.length === 0);
|
|
4285
4285
|
});
|
|
4286
4286
|
break;
|
|
4287
4287
|
}
|
|
4288
|
-
handleResult$1(item, error, result);
|
|
4288
|
+
handleResult$1(item, error, result, this.#items.length === 0);
|
|
4289
4289
|
item = this.#items.shift();
|
|
4290
4290
|
}
|
|
4291
4291
|
this.#runners -= 1;
|
|
@@ -4311,11 +4311,14 @@ function getOptions(input) {
|
|
|
4311
4311
|
maximum: getNumberOrDefault(options.maximum, 0)
|
|
4312
4312
|
};
|
|
4313
4313
|
}
|
|
4314
|
-
function handleResult$1(item, error, result) {
|
|
4314
|
+
function handleResult$1(item, error, result, finished) {
|
|
4315
4315
|
item.signal?.removeEventListener(EVENT_NAME, item.abort);
|
|
4316
4316
|
if (item.signal?.aborted ?? false) item.reject();
|
|
4317
4317
|
else if (error) item.reject(result);
|
|
4318
|
-
else item.resolve(
|
|
4318
|
+
else item.resolve({
|
|
4319
|
+
finished,
|
|
4320
|
+
value: result
|
|
4321
|
+
});
|
|
4319
4322
|
}
|
|
4320
4323
|
function queue(callback, options) {
|
|
4321
4324
|
if (typeof callback !== "function") throw new TypeError(MESSAGE_CALLBACK);
|
package/dist/queue.d.mts
CHANGED
|
@@ -82,8 +82,24 @@ type QueueOptions = {
|
|
|
82
82
|
maximum?: number;
|
|
83
83
|
};
|
|
84
84
|
type Queued<Value> = {
|
|
85
|
+
/**
|
|
86
|
+
* ID of the queued promise _(can be used to remove it from the queue)_
|
|
87
|
+
*/
|
|
85
88
|
readonly id: number;
|
|
86
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Queued promise
|
|
91
|
+
*/
|
|
92
|
+
readonly promise: Promise<QueuedResult<Value>>;
|
|
93
|
+
};
|
|
94
|
+
type QueuedResult<Value> = {
|
|
95
|
+
/**
|
|
96
|
+
* Has the queue finished processing all items?
|
|
97
|
+
*/
|
|
98
|
+
finished: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Result for the queued promise
|
|
101
|
+
*/
|
|
102
|
+
value: Value;
|
|
87
103
|
};
|
|
88
104
|
/**
|
|
89
105
|
* Create a queue for an asynchronous callback function
|
|
@@ -100,4 +116,4 @@ declare function queue<Callback extends GenericAsyncCallback>(callback: Callback
|
|
|
100
116
|
*/
|
|
101
117
|
declare function queue<Callback extends GenericCallback>(callback: Callback, options?: QueueOptions): Queue<Parameters<Callback>, ReturnType<Callback>>;
|
|
102
118
|
//#endregion
|
|
103
|
-
export { type Queue, QueueError, type QueueOptions, type Queued, queue };
|
|
119
|
+
export { type Queue, QueueError, type QueueOptions, type Queued, type QueuedResult, queue };
|
package/dist/queue.mjs
CHANGED
|
@@ -160,11 +160,11 @@ var Queue = class {
|
|
|
160
160
|
if (this.#paused) {
|
|
161
161
|
const paused = item;
|
|
162
162
|
this.#handled.push(() => {
|
|
163
|
-
handleResult(paused, error, result);
|
|
163
|
+
handleResult(paused, error, result, this.#items.length === 0);
|
|
164
164
|
});
|
|
165
165
|
break;
|
|
166
166
|
}
|
|
167
|
-
handleResult(item, error, result);
|
|
167
|
+
handleResult(item, error, result, this.#items.length === 0);
|
|
168
168
|
item = this.#items.shift();
|
|
169
169
|
}
|
|
170
170
|
this.#runners -= 1;
|
|
@@ -190,11 +190,14 @@ function getOptions(input) {
|
|
|
190
190
|
maximum: getNumberOrDefault(options.maximum, 0)
|
|
191
191
|
};
|
|
192
192
|
}
|
|
193
|
-
function handleResult(item, error, result) {
|
|
193
|
+
function handleResult(item, error, result, finished) {
|
|
194
194
|
item.signal?.removeEventListener(EVENT_NAME, item.abort);
|
|
195
195
|
if (item.signal?.aborted ?? false) item.reject();
|
|
196
196
|
else if (error) item.reject(result);
|
|
197
|
-
else item.resolve(
|
|
197
|
+
else item.resolve({
|
|
198
|
+
finished,
|
|
199
|
+
value: result
|
|
200
|
+
});
|
|
198
201
|
}
|
|
199
202
|
function queue(callback, options) {
|
|
200
203
|
if (typeof callback !== "function") throw new TypeError(MESSAGE_CALLBACK);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/atoms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.173.0",
|
|
4
4
|
"description": "Atomic utilities for making your JavaScript better.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helper",
|
|
@@ -229,8 +229,10 @@
|
|
|
229
229
|
"watch": "npx vite build --watch"
|
|
230
230
|
},
|
|
231
231
|
"devDependencies": {
|
|
232
|
+
"@oxlint/plugins": "^1.59",
|
|
232
233
|
"@types/node": "^25.5",
|
|
233
234
|
"@vitest/coverage-istanbul": "^4.1",
|
|
235
|
+
"eslint": "^10.2",
|
|
234
236
|
"jsdom": "^29.0",
|
|
235
237
|
"tsdown": "^0.21",
|
|
236
238
|
"typescript": "^5.9",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node
|
|
3
|
+
* @returns {boolean}
|
|
4
|
+
*/
|
|
5
|
+
function isStringify(node) {
|
|
6
|
+
const {callee} = node;
|
|
7
|
+
|
|
8
|
+
if (callee.name === stringName) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {property} = callee;
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
callee.type !== memberExpression ||
|
|
16
|
+
property == null ||
|
|
17
|
+
property.type !== identifierExpression ||
|
|
18
|
+
property.name !== toStringName
|
|
19
|
+
) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// TODO: check .toString usage
|
|
24
|
+
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const message = "Prefer 'getString' (@oscarpalmer/atoms) instead of 'toString' and 'String'";
|
|
29
|
+
|
|
30
|
+
const selectors = [
|
|
31
|
+
`[callee.name="${stringName}"]`,
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export default {
|
|
35
|
+
message,
|
|
36
|
+
selectors,
|
|
37
|
+
name: 'string.getString',
|
|
38
|
+
value: {
|
|
39
|
+
createOnce(context) {
|
|
40
|
+
return {
|
|
41
|
+
CallExpression(node) {
|
|
42
|
+
if (isStringify(node)) {
|
|
43
|
+
context.report({
|
|
44
|
+
message,
|
|
45
|
+
node,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
meta: {
|
|
52
|
+
docs: {
|
|
53
|
+
description: message,
|
|
54
|
+
},
|
|
55
|
+
type: 'suggestion',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const identifierExpression = 'Identifier';
|
|
61
|
+
|
|
62
|
+
const memberExpression = 'MemberExpression';
|
|
63
|
+
|
|
64
|
+
const stringName = 'String';
|
|
65
|
+
|
|
66
|
+
const toStringName = 'toString';
|
package/plugin/helpers.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* @param {string|undefined} prefix
|
|
26
26
|
* @returns {string}
|
|
27
27
|
*/
|
|
28
|
-
function
|
|
28
|
+
function getMethods(methods, prefix) {
|
|
29
29
|
const array = [...methods];
|
|
30
30
|
const {length} = array;
|
|
31
31
|
|
|
@@ -38,6 +38,8 @@ function getMethodNames(methods, prefix) {
|
|
|
38
38
|
|
|
39
39
|
if (index === 0) {
|
|
40
40
|
names = `'${name}'`;
|
|
41
|
+
} else if (length === 2) {
|
|
42
|
+
names += ` and '${name}'`;
|
|
41
43
|
} else {
|
|
42
44
|
names += index === length - 1 ? `, and '${name}'` : `, '${name}'`;
|
|
43
45
|
}
|
|
@@ -47,24 +49,31 @@ function getMethodNames(methods, prefix) {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
|
-
* @param {
|
|
52
|
+
* @param {string} type
|
|
53
|
+
* @param {string} name
|
|
51
54
|
* @param {Set<string>} methods
|
|
52
|
-
* @param {
|
|
53
|
-
* @param {
|
|
55
|
+
* @param {boolean|undefined} isStatic
|
|
56
|
+
* @param {boolean|undefined} isGlobal
|
|
54
57
|
* @returns {Plugin}
|
|
55
58
|
*/
|
|
56
|
-
export function getPlugin(name, methods,
|
|
57
|
-
const
|
|
59
|
+
export function getPlugin(type, name, methods, isStatic, isGlobal) {
|
|
60
|
+
const globalMethod = isGlobal ?? false;
|
|
61
|
+
const staticMethod = isStatic ?? false;
|
|
62
|
+
|
|
63
|
+
const fullName = `${type}.${name}`;
|
|
64
|
+
const prefix = globalMethod ? undefined : staticMethod ? objects[type] : prefixes[type];
|
|
65
|
+
|
|
66
|
+
const message = `Prefer using '${name}' (@oscarpalmer/atoms) instead of ${getMethods(methods, prefix)}`;
|
|
58
67
|
|
|
59
68
|
return {
|
|
60
69
|
message,
|
|
61
|
-
name:
|
|
62
|
-
selectors: getSelectors(methods),
|
|
70
|
+
name: fullName,
|
|
71
|
+
selectors: getSelectors(type, methods, staticMethod, globalMethod),
|
|
63
72
|
value: {
|
|
64
|
-
|
|
73
|
+
createOnce(context) {
|
|
65
74
|
return {
|
|
66
75
|
CallExpression(node) {
|
|
67
|
-
if (
|
|
76
|
+
if (isCall(type, context, node, methods, staticMethod, globalMethod)) {
|
|
68
77
|
context.report({
|
|
69
78
|
message,
|
|
70
79
|
node,
|
|
@@ -84,20 +93,31 @@ export function getPlugin(name, methods, validator, prefix) {
|
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
/**
|
|
96
|
+
* @param {string} type
|
|
87
97
|
* @param {Set<string>} methods
|
|
98
|
+
* @param {boolean} isStatic
|
|
99
|
+
* @param {boolean} isGlobal
|
|
88
100
|
* @returns {string[]}
|
|
89
101
|
*/
|
|
90
|
-
function getSelectors(methods) {
|
|
102
|
+
function getSelectors(type, methods, isStatic, isGlobal) {
|
|
91
103
|
const array = [...methods];
|
|
92
104
|
const {length} = array;
|
|
93
105
|
|
|
106
|
+
const origin = isGlobal
|
|
107
|
+
? ''
|
|
108
|
+
: isStatic
|
|
109
|
+
? `[callee.object.type="${identifierExpression}"][callee.object.name="${objects[type]}"]`
|
|
110
|
+
: `[callee.object.type="${typeExpressions[type]}"]`;
|
|
111
|
+
|
|
94
112
|
const selectors = [];
|
|
95
113
|
|
|
96
114
|
for (let index = 0; index < length; index += 1) {
|
|
97
115
|
const method = array[index];
|
|
98
116
|
|
|
99
117
|
selectors.push(
|
|
100
|
-
|
|
118
|
+
isGlobal
|
|
119
|
+
? `CallExpression[callee.name="${method}"]`
|
|
120
|
+
: `CallExpression[callee.type="${memberExpression}"]${origin}[callee.property.type="${identifierExpression}"][callee.property.name="${method}"]`,
|
|
101
121
|
);
|
|
102
122
|
}
|
|
103
123
|
|
|
@@ -105,29 +125,106 @@ function getSelectors(methods) {
|
|
|
105
125
|
}
|
|
106
126
|
|
|
107
127
|
/**
|
|
128
|
+
* @param {string} type
|
|
108
129
|
* @param {import('eslint').Rule.RuleContext} context
|
|
109
130
|
* @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node
|
|
110
131
|
* @param {Set<string>} methods
|
|
132
|
+
* @param {boolean} isStatic
|
|
133
|
+
* @param {boolean} isGlobal
|
|
111
134
|
* @returns {boolean}
|
|
112
135
|
*/
|
|
113
|
-
|
|
136
|
+
function isCall(type, context, node, methods, isStatic, isGlobal) {
|
|
114
137
|
const {callee} = node;
|
|
115
138
|
|
|
116
|
-
if (
|
|
139
|
+
if (isGlobal) {
|
|
140
|
+
return methods.has(callee.name);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (callee.type !== memberExpression || !methods.has(callee.property.name)) {
|
|
117
144
|
return false;
|
|
118
145
|
}
|
|
119
146
|
|
|
120
|
-
|
|
147
|
+
return isStatic
|
|
148
|
+
? isStaticMethod(type, context, node, methods)
|
|
149
|
+
: isInstanceMethod(type, context, node, methods);
|
|
150
|
+
}
|
|
121
151
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
152
|
+
/**
|
|
153
|
+
* @param {string} type
|
|
154
|
+
* @param {import('eslint').Rule.RuleContext} context
|
|
155
|
+
* @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node
|
|
156
|
+
* @param {Set<string>} methods
|
|
157
|
+
* @returns {boolean}
|
|
158
|
+
*/
|
|
159
|
+
function isInstanceMethod(type, context, node, methods) {
|
|
160
|
+
const {object, property} = node.callee;
|
|
161
|
+
|
|
162
|
+
if (object.type === literalExpression) {
|
|
163
|
+
return isLiteralMethod(type, context, node, methods);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (object.type !== typeExpressions[type]) {
|
|
167
|
+
return property.type === identifierExpression && isLiteralMethod(type, context, node, methods);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return property.type === identifierExpression;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {string} type
|
|
175
|
+
* @param {import('eslint').Rule.RuleContext} context
|
|
176
|
+
* @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node
|
|
177
|
+
* @param {Set<string>} methods
|
|
178
|
+
* @returns {boolean}
|
|
179
|
+
*/
|
|
180
|
+
function isLiteralMethod(type, context, node, methods) {
|
|
181
|
+
// TODO: check literal value
|
|
182
|
+
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {string} type
|
|
188
|
+
* @param {import('eslint').Rule.RuleContext} context
|
|
189
|
+
* @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node
|
|
190
|
+
* @param {Set<string>} methods
|
|
191
|
+
* @returns {boolean}
|
|
192
|
+
*/
|
|
193
|
+
function isStaticMethod(type, context, node, methods) {
|
|
194
|
+
const {object, property} = node.callee;
|
|
195
|
+
|
|
196
|
+
if (object.type !== identifierExpression || object.name !== objects[type]) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return property.type === identifierExpression;
|
|
127
201
|
}
|
|
128
202
|
|
|
129
203
|
const arrayExpression = 'ArrayExpression';
|
|
130
204
|
|
|
205
|
+
const identifierExpression = 'Identifier';
|
|
206
|
+
|
|
207
|
+
const literalExpression = 'Literal';
|
|
208
|
+
|
|
131
209
|
const memberExpression = 'MemberExpression';
|
|
132
210
|
|
|
133
|
-
const
|
|
211
|
+
const objectExpression = 'ObjectExpression';
|
|
212
|
+
|
|
213
|
+
const objects = {
|
|
214
|
+
array: 'Array',
|
|
215
|
+
math: 'Math',
|
|
216
|
+
promise: 'Promise',
|
|
217
|
+
string: 'String',
|
|
218
|
+
value: 'Object',
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const prefixes = {
|
|
222
|
+
array: 'Array.prototype',
|
|
223
|
+
string: 'String.prototype',
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const typeExpressions = {
|
|
227
|
+
array: arrayExpression,
|
|
228
|
+
string: literalExpression,
|
|
229
|
+
value: objectExpression,
|
|
230
|
+
};
|
package/plugin/index.js
CHANGED
|
@@ -1,16 +1,60 @@
|
|
|
1
|
+
import {eslintCompatPlugin} from '@oxlint/plugins';
|
|
1
2
|
import {groupBy} from '../dist/array/group-by.mjs';
|
|
2
|
-
import
|
|
3
|
+
import pkg from '../package.json' with {type: 'json'};
|
|
4
|
+
import getStringPlugin from './get-string.js';
|
|
5
|
+
import {getPlugin} from './helpers.js';
|
|
6
|
+
import tryCatchPlugin from './try-catch.js';
|
|
7
|
+
|
|
8
|
+
export const arrayPlugins = [
|
|
9
|
+
getPlugin('array', 'exists', new Set(['includes'])),
|
|
10
|
+
getPlugin('array', 'filter', new Set(['filter'])),
|
|
11
|
+
getPlugin('array', 'find', new Set(['find'])),
|
|
12
|
+
getPlugin('array', 'flatten', new Set(['flat'])),
|
|
13
|
+
getPlugin('array', 'indexOf', new Set(['findIndex', 'indexOf'])),
|
|
14
|
+
getPlugin('array', 'push', new Set(['push'])),
|
|
15
|
+
getPlugin('array', 'slice', new Set(['slice'])),
|
|
16
|
+
getPlugin('array', 'sort', new Set(['sort'])),
|
|
17
|
+
getPlugin('array', 'splice', new Set(['splice'])),
|
|
18
|
+
getPlugin('array', 'times', new Set(['from']), true),
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export const mathPlugins = [
|
|
22
|
+
getPlugin('math', 'floor', new Set(['floor']), true),
|
|
23
|
+
getPlugin('math', 'max', new Set(['max']), true),
|
|
24
|
+
getPlugin('math', 'min', new Set(['min']), true),
|
|
25
|
+
getPlugin('math', 'round', new Set(['round']), true),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const miscPlugins = [
|
|
29
|
+
tryCatchPlugin,
|
|
30
|
+
getPlugin('number', 'getNumber', new Set(['Number']), true, true),
|
|
31
|
+
getPlugin('promise', 'promises', new Set(['all', 'allSettled']), true),
|
|
32
|
+
getPlugin('value', 'clone', new Set(['structuredClone']), true, true),
|
|
33
|
+
getPlugin('value', 'equal', new Set(['is']), true),
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export const stringPlugins = [
|
|
37
|
+
getStringPlugin,
|
|
38
|
+
getPlugin('string', 'endsWith', new Set(['endsWith'])),
|
|
39
|
+
getPlugin('string', 'includes', new Set(['includes'])),
|
|
40
|
+
getPlugin('string', 'lowerCase', new Set(['toLowerCase'])),
|
|
41
|
+
getPlugin('string', 'startsWith', new Set(['startsWith'])),
|
|
42
|
+
getPlugin('string', 'upperCase', new Set(['toUpperCase'])),
|
|
43
|
+
];
|
|
3
44
|
|
|
4
45
|
/**
|
|
5
46
|
* @typedef {import('eslint').Linter.Config} ESLintConfig
|
|
6
47
|
*/
|
|
7
|
-
export default {
|
|
48
|
+
export default eslintCompatPlugin({
|
|
8
49
|
configs: {},
|
|
9
50
|
meta: {
|
|
10
51
|
name: '@oscarpalmer/atoms',
|
|
11
|
-
version:
|
|
52
|
+
version: pkg.version,
|
|
12
53
|
},
|
|
13
54
|
rules: {
|
|
14
|
-
...groupBy(
|
|
55
|
+
...groupBy(arrayPlugins, 'name', 'value'),
|
|
56
|
+
...groupBy(mathPlugins, 'name', 'value'),
|
|
57
|
+
...groupBy(miscPlugins, 'name', 'value'),
|
|
58
|
+
...groupBy(stringPlugins, 'name', 'value'),
|
|
15
59
|
},
|
|
16
|
-
};
|
|
60
|
+
});
|
package/plugin/rules.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {Record<string, 'warn'>}
|
|
3
|
+
*/
|
|
4
|
+
const rules = {
|
|
5
|
+
'@oscarpalmer/atoms/array.exists': 'warn',
|
|
6
|
+
'@oscarpalmer/atoms/array.filter': 'warn',
|
|
7
|
+
'@oscarpalmer/atoms/array.find': 'warn',
|
|
8
|
+
'@oscarpalmer/atoms/array.flatten': 'warn',
|
|
9
|
+
'@oscarpalmer/atoms/array.indexOf': 'warn',
|
|
10
|
+
'@oscarpalmer/atoms/array.push': 'warn',
|
|
11
|
+
'@oscarpalmer/atoms/array.slice': 'warn',
|
|
12
|
+
'@oscarpalmer/atoms/array.sort': 'warn',
|
|
13
|
+
'@oscarpalmer/atoms/array.splice': 'warn',
|
|
14
|
+
'@oscarpalmer/atoms/array.times': 'warn',
|
|
15
|
+
'@oscarpalmer/atoms/attempt': 'warn',
|
|
16
|
+
'@oscarpalmer/atoms/math.floor': 'warn',
|
|
17
|
+
'@oscarpalmer/atoms/math.max': 'warn',
|
|
18
|
+
'@oscarpalmer/atoms/math.min': 'warn',
|
|
19
|
+
'@oscarpalmer/atoms/math.round': 'warn',
|
|
20
|
+
'@oscarpalmer/atoms/number.getNumber': 'warn',
|
|
21
|
+
'@oscarpalmer/atoms/promise.promises': 'warn',
|
|
22
|
+
'@oscarpalmer/atoms/string.endsWith': 'warn',
|
|
23
|
+
'@oscarpalmer/atoms/string.getString': 'warn',
|
|
24
|
+
'@oscarpalmer/atoms/string.includes': 'warn',
|
|
25
|
+
'@oscarpalmer/atoms/string.lowerCase': 'warn',
|
|
26
|
+
'@oscarpalmer/atoms/string.startsWith': 'warn',
|
|
27
|
+
'@oscarpalmer/atoms/string.upperCase': 'warn',
|
|
28
|
+
'@oscarpalmer/atoms/value.clone': 'warn',
|
|
29
|
+
'@oscarpalmer/atoms/value.equal': 'warn',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default rules;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node
|
|
3
|
+
* @returns {boolean}
|
|
4
|
+
*/
|
|
5
|
+
function isTryCatch(node) {
|
|
6
|
+
if (node.parent == null) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (node.parent.type === statement) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return isTryCatch(node.parent);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const message = "Prefer 'attempt' (@oscarpalmer/atoms) instead of try-catch blocks";
|
|
18
|
+
|
|
19
|
+
const selector = 'TryStatement > BlockStatement';
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
message,
|
|
23
|
+
name: 'attempt',
|
|
24
|
+
selectors: [selector],
|
|
25
|
+
value: {
|
|
26
|
+
createOnce(context) {
|
|
27
|
+
return {
|
|
28
|
+
CallExpression(node) {
|
|
29
|
+
if (isTryCatch(node)) {
|
|
30
|
+
context.report({
|
|
31
|
+
message,
|
|
32
|
+
node,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
meta: {
|
|
39
|
+
docs: {
|
|
40
|
+
description: message,
|
|
41
|
+
},
|
|
42
|
+
type: 'suggestion',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const statement = 'TryStatement';
|
package/src/queue.ts
CHANGED
|
@@ -100,9 +100,9 @@ class Queue<CallbackParameters extends Parameters<GenericAsyncCallback>, Callbac
|
|
|
100
100
|
const id = this.#identify();
|
|
101
101
|
|
|
102
102
|
let rejector: (reason?: unknown) => void;
|
|
103
|
-
let resolver: (value: CallbackResult) => void;
|
|
103
|
+
let resolver: (value: QueuedResult<CallbackResult>) => void;
|
|
104
104
|
|
|
105
|
-
const promise = new Promise<CallbackResult
|
|
105
|
+
const promise = new Promise<QueuedResult<CallbackResult>>((resolve, reject) => {
|
|
106
106
|
rejector = reject;
|
|
107
107
|
resolver = resolve;
|
|
108
108
|
});
|
|
@@ -225,13 +225,13 @@ class Queue<CallbackParameters extends Parameters<GenericAsyncCallback>, Callbac
|
|
|
225
225
|
const paused = item;
|
|
226
226
|
|
|
227
227
|
this.#handled.push(() => {
|
|
228
|
-
handleResult(paused, error, result);
|
|
228
|
+
handleResult(paused, error, result, this.#items.length === 0);
|
|
229
229
|
});
|
|
230
230
|
|
|
231
231
|
break;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
handleResult(item, error, result);
|
|
234
|
+
handleResult(item, error, result, this.#items.length === 0);
|
|
235
235
|
|
|
236
236
|
item = this.#items.shift();
|
|
237
237
|
}
|
|
@@ -264,20 +264,37 @@ type QueueOptions = {
|
|
|
264
264
|
};
|
|
265
265
|
|
|
266
266
|
type Queued<Value> = {
|
|
267
|
+
/**
|
|
268
|
+
* ID of the queued promise _(can be used to remove it from the queue)_
|
|
269
|
+
*/
|
|
267
270
|
readonly id: number;
|
|
268
|
-
|
|
271
|
+
/**
|
|
272
|
+
* Queued promise
|
|
273
|
+
*/
|
|
274
|
+
readonly promise: Promise<QueuedResult<Value>>;
|
|
269
275
|
};
|
|
270
276
|
|
|
271
277
|
type QueuedItem<CallbackParameters extends Parameters<GenericAsyncCallback>, CallbackResult> = {
|
|
272
278
|
abort?: () => void;
|
|
273
279
|
id: number;
|
|
274
280
|
parameters: CallbackParameters;
|
|
275
|
-
promise: Promise<CallbackResult
|
|
281
|
+
promise: Promise<QueuedResult<CallbackResult>>;
|
|
276
282
|
reject: (reason?: unknown) => void;
|
|
277
|
-
resolve: (value: CallbackResult) => void;
|
|
283
|
+
resolve: (value: QueuedResult<CallbackResult>) => void;
|
|
278
284
|
signal?: AbortSignal;
|
|
279
285
|
};
|
|
280
286
|
|
|
287
|
+
type QueuedResult<Value> = {
|
|
288
|
+
/**
|
|
289
|
+
* Has the queue finished processing all items?
|
|
290
|
+
*/
|
|
291
|
+
finished: boolean;
|
|
292
|
+
/**
|
|
293
|
+
* Result for the queued promise
|
|
294
|
+
*/
|
|
295
|
+
value: Value;
|
|
296
|
+
};
|
|
297
|
+
|
|
281
298
|
// #endregion
|
|
282
299
|
|
|
283
300
|
// #region Functions
|
|
@@ -304,6 +321,7 @@ function handleResult<CallbackParameters extends Parameters<GenericAsyncCallback
|
|
|
304
321
|
item: QueuedItem<CallbackParameters, CallbackResult>,
|
|
305
322
|
error: boolean,
|
|
306
323
|
result: unknown,
|
|
324
|
+
finished: boolean,
|
|
307
325
|
): void {
|
|
308
326
|
item.signal?.removeEventListener(EVENT_NAME, item.abort!);
|
|
309
327
|
|
|
@@ -313,7 +331,10 @@ function handleResult<CallbackParameters extends Parameters<GenericAsyncCallback
|
|
|
313
331
|
if (error) {
|
|
314
332
|
item.reject(result);
|
|
315
333
|
} else {
|
|
316
|
-
item.resolve(
|
|
334
|
+
item.resolve({
|
|
335
|
+
finished,
|
|
336
|
+
value: result as CallbackResult,
|
|
337
|
+
});
|
|
317
338
|
}
|
|
318
339
|
}
|
|
319
340
|
}
|
|
@@ -373,6 +394,6 @@ const MESSAGE_REMOVE = 'Item removed from queue';
|
|
|
373
394
|
|
|
374
395
|
// #region Exports
|
|
375
396
|
|
|
376
|
-
export {queue, QueueError, type Queue, type Queued, type QueueOptions};
|
|
397
|
+
export {queue, QueueError, type Queue, type Queued, type QueueOptions, type QueuedResult};
|
|
377
398
|
|
|
378
399
|
// #endregion
|
package/plugin/array.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import {getPlugin, isArrayMethod} from './helpers.js';
|
|
2
|
-
|
|
3
|
-
export default [
|
|
4
|
-
getPlugin(
|
|
5
|
-
{
|
|
6
|
-
full: 'array.exists',
|
|
7
|
-
short: 'exists',
|
|
8
|
-
},
|
|
9
|
-
new Set(['includes']),
|
|
10
|
-
isArrayMethod,
|
|
11
|
-
'Array.prototype',
|
|
12
|
-
),
|
|
13
|
-
getPlugin(
|
|
14
|
-
{
|
|
15
|
-
full: 'array.sort',
|
|
16
|
-
short: 'sort',
|
|
17
|
-
},
|
|
18
|
-
new Set(['sort']),
|
|
19
|
-
isArrayMethod,
|
|
20
|
-
'Array.prototype',
|
|
21
|
-
),
|
|
22
|
-
];
|