@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 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
- readonly promise: Promise<Value>;
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(result);
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
- readonly promise: Promise<Value>;
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(result);
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.172.3",
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 getMethodNames(methods, prefix) {
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 {Name} name
52
+ * @param {string} type
53
+ * @param {string} name
51
54
  * @param {Set<string>} methods
52
- * @param {Validator} validator
53
- * @param {string|undefined} prefix
55
+ * @param {boolean|undefined} isStatic
56
+ * @param {boolean|undefined} isGlobal
54
57
  * @returns {Plugin}
55
58
  */
56
- export function getPlugin(name, methods, validator, prefix) {
57
- const message = `Prefer using '${name.short}' (@oscarpalmer/atoms) instead of ${getMethodNames(methods, prefix)}`;
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: name.full,
62
- selectors: getSelectors(methods),
70
+ name: fullName,
71
+ selectors: getSelectors(type, methods, staticMethod, globalMethod),
63
72
  value: {
64
- create(context) {
73
+ createOnce(context) {
65
74
  return {
66
75
  CallExpression(node) {
67
- if (validator(context, node, methods)) {
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
- `CallExpression[callee.type="${memberExpression}"][callee.object.type="${arrayExpression}"][callee.property.type="${identifierType}"][callee.property.name="${method}"]`,
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
- export function isArrayMethod(context, node, methods) {
136
+ function isCall(type, context, node, methods, isStatic, isGlobal) {
114
137
  const {callee} = node;
115
138
 
116
- if (callee.type !== memberExpression) {
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
- const {object, property} = callee;
147
+ return isStatic
148
+ ? isStaticMethod(type, context, node, methods)
149
+ : isInstanceMethod(type, context, node, methods);
150
+ }
121
151
 
122
- return (
123
- object.type === arrayExpression &&
124
- property.type === identifierType &&
125
- methods.has(property.name)
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 identifierType = 'Identifier';
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 array from './array.js';
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: '1.0.0',
52
+ version: pkg.version,
12
53
  },
13
54
  rules: {
14
- ...groupBy(array, 'name', 'value'),
55
+ ...groupBy(arrayPlugins, 'name', 'value'),
56
+ ...groupBy(mathPlugins, 'name', 'value'),
57
+ ...groupBy(miscPlugins, 'name', 'value'),
58
+ ...groupBy(stringPlugins, 'name', 'value'),
15
59
  },
16
- };
60
+ });
@@ -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>((resolve, reject) => {
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
- readonly promise: Promise<Value>;
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(result as CallbackResult);
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
- ];