@dereekb/util 13.11.2 → 13.11.4

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.
Files changed (102) hide show
  1. package/eslint/index.cjs.default.js +1 -0
  2. package/eslint/index.cjs.js +687 -0
  3. package/eslint/index.cjs.mjs +2 -0
  4. package/eslint/index.d.ts +1 -0
  5. package/eslint/index.esm.js +683 -0
  6. package/eslint/package.json +23 -0
  7. package/eslint/src/index.d.ts +1 -0
  8. package/eslint/src/lib/comments.d.ts +101 -0
  9. package/eslint/src/lib/index.d.ts +3 -0
  10. package/eslint/src/lib/plugin.d.ts +18 -0
  11. package/eslint/src/lib/prefer-no-side-effects-in-jsdoc.rule.d.ts +50 -0
  12. package/eslint/src/lib/require-no-side-effects.rule.d.ts +67 -0
  13. package/fetch/package.json +2 -2
  14. package/index.cjs.js +1421 -23
  15. package/index.esm.js +1421 -24
  16. package/package.json +7 -1
  17. package/src/lib/array/array.factory.d.ts +2 -0
  18. package/src/lib/array/array.filter.d.ts +50 -17
  19. package/src/lib/array/array.find.d.ts +1 -0
  20. package/src/lib/array/array.index.d.ts +7 -0
  21. package/src/lib/array/array.indexed.d.ts +21 -0
  22. package/src/lib/array/array.make.d.ts +7 -0
  23. package/src/lib/array/array.random.d.ts +1 -0
  24. package/src/lib/array/array.unique.d.ts +3 -0
  25. package/src/lib/array/array.value.d.ts +7 -0
  26. package/src/lib/auth/auth.role.claims.d.ts +7 -0
  27. package/src/lib/boolean.d.ts +1 -0
  28. package/src/lib/contact/random.d.ts +14 -0
  29. package/src/lib/date/date.d.ts +229 -0
  30. package/src/lib/date/time.d.ts +7 -0
  31. package/src/lib/date/week.d.ts +7 -0
  32. package/src/lib/error/error.d.ts +7 -0
  33. package/src/lib/filter/filter.d.ts +7 -0
  34. package/src/lib/function/function.boolean.d.ts +7 -0
  35. package/src/lib/function/function.forward.d.ts +14 -0
  36. package/src/lib/getter/getter.cache.d.ts +7 -0
  37. package/src/lib/getter/getter.d.ts +34 -0
  38. package/src/lib/getter/getter.map.d.ts +7 -0
  39. package/src/lib/getter/getter.util.d.ts +7 -0
  40. package/src/lib/grouping.d.ts +8 -0
  41. package/src/lib/hash.d.ts +1 -0
  42. package/src/lib/key.d.ts +16 -0
  43. package/src/lib/map/map.key.d.ts +14 -0
  44. package/src/lib/model/id.batch.d.ts +7 -0
  45. package/src/lib/model/id.factory.d.ts +7 -0
  46. package/src/lib/model/model.conversion.d.ts +35 -0
  47. package/src/lib/model/model.copy.d.ts +7 -0
  48. package/src/lib/model/model.d.ts +19 -0
  49. package/src/lib/model/model.modify.d.ts +14 -0
  50. package/src/lib/nodejs/stream.d.ts +7 -0
  51. package/src/lib/number/bound.d.ts +3 -0
  52. package/src/lib/number/dollar.d.ts +7 -0
  53. package/src/lib/number/factory.d.ts +7 -0
  54. package/src/lib/number/random.d.ts +1 -0
  55. package/src/lib/number/round.d.ts +22 -0
  56. package/src/lib/number/sort.d.ts +7 -0
  57. package/src/lib/number/transform.d.ts +7 -0
  58. package/src/lib/object/object.array.delta.d.ts +7 -0
  59. package/src/lib/object/object.equal.d.ts +7 -0
  60. package/src/lib/object/object.filter.pojo.d.ts +87 -0
  61. package/src/lib/object/object.filter.tuple.d.ts +16 -0
  62. package/src/lib/object/object.key.d.ts +14 -0
  63. package/src/lib/object/object.map.d.ts +14 -0
  64. package/src/lib/path/path.d.ts +9 -0
  65. package/src/lib/promise/promise.d.ts +21 -0
  66. package/src/lib/promise/promise.factory.d.ts +7 -0
  67. package/src/lib/promise/promise.task.d.ts +7 -0
  68. package/src/lib/service/handler.config.d.ts +28 -0
  69. package/src/lib/service/handler.d.ts +14 -0
  70. package/src/lib/set/set.d.ts +21 -0
  71. package/src/lib/set/set.decision.d.ts +7 -0
  72. package/src/lib/set/set.delta.d.ts +7 -0
  73. package/src/lib/set/set.selection.d.ts +7 -0
  74. package/src/lib/sort.d.ts +8 -0
  75. package/src/lib/string/char.d.ts +7 -0
  76. package/src/lib/string/dencoder.d.ts +35 -0
  77. package/src/lib/string/factory.d.ts +22 -1
  78. package/src/lib/string/replace.d.ts +78 -0
  79. package/src/lib/string/search.d.ts +7 -0
  80. package/src/lib/string/sort.d.ts +7 -0
  81. package/src/lib/string/string.d.ts +1 -0
  82. package/src/lib/string/transform.d.ts +53 -0
  83. package/src/lib/string/tree.d.ts +7 -0
  84. package/src/lib/string/url.d.ts +7 -0
  85. package/src/lib/tree/tree.array.d.ts +1 -0
  86. package/src/lib/tree/tree.explore.d.ts +3 -0
  87. package/src/lib/type.d.ts +3 -2
  88. package/src/lib/value/bound.d.ts +28 -0
  89. package/src/lib/value/comparator.d.ts +16 -0
  90. package/src/lib/value/decision.d.ts +5 -0
  91. package/src/lib/value/equal.d.ts +2 -0
  92. package/src/lib/value/indexed.d.ts +127 -0
  93. package/src/lib/value/map.d.ts +22 -0
  94. package/src/lib/value/maybe.type.d.ts +2 -2
  95. package/src/lib/value/modifier.d.ts +13 -0
  96. package/src/lib/value/point.d.ts +56 -0
  97. package/src/lib/value/use.d.ts +37 -0
  98. package/src/lib/value/vector.d.ts +7 -0
  99. package/test/index.cjs.js +17 -4
  100. package/test/index.esm.js +17 -4
  101. package/test/package.json +2 -2
  102. package/test/src/lib/shared/shared.fail.d.ts +24 -5
@@ -0,0 +1,687 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * The bundler hint string that marks a function call as side-effect-free.
5
+ */ var NO_SIDE_EFFECTS_TAG = '@__NO_SIDE_EFFECTS__';
6
+ /**
7
+ * Returns true if the given comment text contains the @__NO_SIDE_EFFECTS__ marker.
8
+ *
9
+ * @param text - The comment body text (without the `/*` and `*\/` delimiters).
10
+ * @returns True when the marker substring is present.
11
+ */ function commentContainsNoSideEffects(text) {
12
+ return text.includes(NO_SIDE_EFFECTS_TAG);
13
+ }
14
+ /**
15
+ * Returns the leading whitespace (column indent) of the line containing the given offset.
16
+ *
17
+ * @param sourceText - The full source text being inspected.
18
+ * @param offset - A character offset into `sourceText` indicating the line of interest.
19
+ * @returns The whitespace prefix (spaces/tabs) of that line.
20
+ */ function getLineIndent(sourceText, offset) {
21
+ var lineStart = offset;
22
+ while(lineStart > 0 && sourceText.charAt(lineStart - 1) !== '\n'){
23
+ lineStart -= 1;
24
+ }
25
+ var cursor = lineStart;
26
+ while(cursor < sourceText.length && (sourceText.charAt(cursor) === ' ' || sourceText.charAt(cursor) === '\t')){
27
+ cursor += 1;
28
+ }
29
+ return sourceText.slice(lineStart, cursor);
30
+ }
31
+ /**
32
+ * Returns the outermost statement node for a FunctionDeclaration — its `ExportNamedDeclaration`
33
+ * or `ExportDefaultDeclaration` parent if exported, otherwise the declaration itself. This is the
34
+ * node ESLint attaches leading comments to.
35
+ *
36
+ * @param node - The FunctionDeclaration AST node.
37
+ * @returns The statement node ESLint attaches leading comments to.
38
+ */ function getStatementAnchor(node) {
39
+ return node.parent && (node.parent.type === 'ExportNamedDeclaration' || node.parent.type === 'ExportDefaultDeclaration') ? node.parent : node;
40
+ }
41
+ /**
42
+ * Returns true if the statement is an overload signature (TSDeclareFunction) sharing the given name.
43
+ *
44
+ * @param stmt - The statement node to check (may be an export wrapper).
45
+ * @param name - The function identifier name to match.
46
+ * @returns True when `stmt` is an overload signature for `name`.
47
+ */ function isOverloadSignature(stmt, name) {
48
+ var _inner_id;
49
+ var inner = stmt.type === 'ExportNamedDeclaration' || stmt.type === 'ExportDefaultDeclaration' ? stmt.declaration : stmt;
50
+ return (inner === null || inner === void 0 ? void 0 : inner.type) === 'TSDeclareFunction' && ((_inner_id = inner.id) === null || _inner_id === void 0 ? void 0 : _inner_id.type) === 'Identifier' && inner.id.name === name;
51
+ }
52
+ /**
53
+ * Walks backward from the implementation FunctionDeclaration through any overload signatures
54
+ * with the same name, collecting:
55
+ *
56
+ * - The leading JSDoc block (preferring the one attached to the **first** overload, since that's
57
+ * where the function's documentation conventionally lives).
58
+ * - All orphan `@__NO_SIDE_EFFECTS__` line/block comments encountered between overloads or
59
+ * between the last overload and the implementation.
60
+ *
61
+ * This handles the common pattern:
62
+ *
63
+ * ```ts
64
+ * \/\*\* doc \*\/
65
+ * export function foo(a: number): number;
66
+ * export function foo(a: string): string;
67
+ * \/\/ \@__NO_SIDE_EFFECTS__
68
+ * export function foo(a: any) { ... }
69
+ * ```
70
+ *
71
+ * @param sourceCode - The ESLint `SourceCode` object used to read leading comments.
72
+ * @param implNode - The implementation FunctionDeclaration node.
73
+ * @returns The leading JSDoc (if any) and any orphan side-effect annotation comments.
74
+ */ function findFunctionLeadingContext(sourceCode, implNode) {
75
+ var _implNode_id;
76
+ if (((_implNode_id = implNode.id) === null || _implNode_id === void 0 ? void 0 : _implNode_id.type) !== 'Identifier') {
77
+ return {
78
+ jsdoc: null,
79
+ orphanLineComments: [],
80
+ hasOverloads: false,
81
+ implLineComment: null,
82
+ implHasSurvivingAnnotation: false,
83
+ chainStartStatement: getStatementAnchor(implNode)
84
+ };
85
+ }
86
+ var name = implNode.id.name;
87
+ var implStmt = getStatementAnchor(implNode);
88
+ var container = implStmt.parent;
89
+ var chainStartIdx = -1;
90
+ var implIdx = -1;
91
+ if (container && Array.isArray(container.body)) {
92
+ implIdx = container.body.indexOf(implStmt);
93
+ if (implIdx >= 0) {
94
+ chainStartIdx = implIdx;
95
+ for(var i = implIdx - 1; i >= 0; i -= 1){
96
+ if (isOverloadSignature(container.body[i], name)) {
97
+ chainStartIdx = i;
98
+ } else {
99
+ break;
100
+ }
101
+ }
102
+ }
103
+ }
104
+ var hasOverloads = chainStartIdx >= 0 && implIdx >= 0 && chainStartIdx < implIdx;
105
+ var firstJsdoc = null;
106
+ var anyJsdocHasNoSideEffects = false;
107
+ var implJsdocHasNoSideEffects = false;
108
+ var orphanLineComments = [];
109
+
110
+ // declaration is required (TS erases overload signatures, so only this annotation survives).
111
+ // Track it separately so callers don't accidentally remove it.
112
+ var implLineComment = null;
113
+ function processCommentsForStatement(stmt, isImplStatement) {
114
+ var comments = sourceCode.getCommentsBefore(stmt) || [];
115
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
116
+ try {
117
+ for(var _iterator = comments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
118
+ var comment = _step.value;
119
+ if (comment.type === 'Block' && comment.value.startsWith('*')) {
120
+ var hasMarker = commentContainsNoSideEffects(comment.value);
121
+ if (hasMarker) {
122
+ anyJsdocHasNoSideEffects = true;
123
+ if (isImplStatement) {
124
+ implJsdocHasNoSideEffects = true;
125
+ }
126
+ }
127
+ // Auto-fix target: the first JSDoc in the chain (where the function's docs conventionally live).
128
+ if (!firstJsdoc) {
129
+ firstJsdoc = {
130
+ node: comment,
131
+ text: comment.value,
132
+ hasNoSideEffects: hasMarker
133
+ };
134
+ }
135
+ } else if (commentContainsNoSideEffects(comment.value)) {
136
+ // Only the impl-leading annotation on an overloaded function is "required"; others are orphans.
137
+ // When multiple line/block comments stack above the impl, keep the closest one (last in source
138
+ // order) as the canonical impl annotation and treat the rest as orphans to consolidate.
139
+ if (isImplStatement && hasOverloads) {
140
+ if (implLineComment) {
141
+ orphanLineComments.push(implLineComment);
142
+ }
143
+ implLineComment = comment;
144
+ } else {
145
+ orphanLineComments.push(comment);
146
+ }
147
+ }
148
+ }
149
+ } catch (err) {
150
+ _didIteratorError = true;
151
+ _iteratorError = err;
152
+ } finally{
153
+ try {
154
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
155
+ _iterator.return();
156
+ }
157
+ } finally{
158
+ if (_didIteratorError) {
159
+ throw _iteratorError;
160
+ }
161
+ }
162
+ }
163
+ }
164
+ if (chainStartIdx >= 0 && implIdx >= 0) {
165
+ for(var i1 = chainStartIdx; i1 <= implIdx; i1 += 1){
166
+ processCommentsForStatement(container.body[i1], i1 === implIdx);
167
+ }
168
+ } else {
169
+ // Fallback: no container body found; just look at comments before the implementation.
170
+ processCommentsForStatement(implStmt, false);
171
+ }
172
+ // Report the first JSDoc as the canonical jsdoc, but reflect any-in-chain satisfaction
173
+ // so callers don't re-annotate when the marker is already present elsewhere in the chain.
174
+ var resolved = null;
175
+ var captured = firstJsdoc;
176
+ if (captured) {
177
+ resolved = {
178
+ node: captured.node,
179
+ text: captured.text,
180
+ hasNoSideEffects: anyJsdocHasNoSideEffects
181
+ };
182
+ }
183
+ // The implementation's emitted JS carries the marker when:
184
+ // - non-overloaded: the function's (only) JSDoc has the tag (it's directly attached to the impl), OR
185
+ // - overloaded: a line/block comment sits above the impl, OR the impl has its own tagged JSDoc.
186
+ var implHasSurvivingAnnotation = hasOverloads ? implLineComment !== null || implJsdocHasNoSideEffects : anyJsdocHasNoSideEffects;
187
+ var chainStartStatement = chainStartIdx >= 0 && container && Array.isArray(container.body) ? container.body[chainStartIdx] : implStmt;
188
+ return {
189
+ jsdoc: resolved,
190
+ orphanLineComments: orphanLineComments,
191
+ hasOverloads: hasOverloads,
192
+ implLineComment: implLineComment,
193
+ implHasSurvivingAnnotation: implHasSurvivingAnnotation,
194
+ chainStartStatement: chainStartStatement
195
+ };
196
+ }
197
+
198
+ function _array_like_to_array$1(arr, len) {
199
+ if (len == null || len > arr.length) len = arr.length;
200
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
201
+ return arr2;
202
+ }
203
+ function _array_with_holes$1(arr) {
204
+ if (Array.isArray(arr)) return arr;
205
+ }
206
+ function _array_without_holes(arr) {
207
+ if (Array.isArray(arr)) return _array_like_to_array$1(arr);
208
+ }
209
+ function _iterable_to_array(iter) {
210
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
211
+ }
212
+ function _iterable_to_array_limit$1(arr, i) {
213
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
214
+ if (_i == null) return;
215
+ var _arr = [];
216
+ var _n = true;
217
+ var _d = false;
218
+ var _s, _e;
219
+ try {
220
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
221
+ _arr.push(_s.value);
222
+ if (i && _arr.length === i) break;
223
+ }
224
+ } catch (err) {
225
+ _d = true;
226
+ _e = err;
227
+ } finally{
228
+ try {
229
+ if (!_n && _i["return"] != null) _i["return"]();
230
+ } finally{
231
+ if (_d) throw _e;
232
+ }
233
+ }
234
+ return _arr;
235
+ }
236
+ function _non_iterable_rest$1() {
237
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
238
+ }
239
+ function _non_iterable_spread() {
240
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
241
+ }
242
+ function _sliced_to_array$1(arr, i) {
243
+ return _array_with_holes$1(arr) || _iterable_to_array_limit$1(arr, i) || _unsupported_iterable_to_array$1(arr, i) || _non_iterable_rest$1();
244
+ }
245
+ function _to_consumable_array(arr) {
246
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array$1(arr) || _non_iterable_spread();
247
+ }
248
+ function _unsupported_iterable_to_array$1(o, minLen) {
249
+ if (!o) return;
250
+ if (typeof o === "string") return _array_like_to_array$1(o, minLen);
251
+ var n = Object.prototype.toString.call(o).slice(8, -1);
252
+ if (n === "Object" && o.constructor) n = o.constructor.name;
253
+ if (n === "Map" || n === "Set") return Array.from(n);
254
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$1(o, minLen);
255
+ }
256
+ /**
257
+ * The JSDoc tag identifying a function as a factory in the @dereekb conventions.
258
+ */ var FACTORY_JSDOC_TAG = '@dbxUtilKind factory';
259
+ /**
260
+ * Default suffix patterns considered factory-like by name when `checkNamePatterns` is enabled.
261
+ */ var DEFAULT_NAME_PATTERNS = [
262
+ /(?:Factory|Factories|Service|Services|Function|Functions)$/,
263
+ /^(?:make|build|create)[A-Z]/,
264
+ /^(?:firestore|optionalFirestore)[A-Z]/
265
+ ];
266
+ /**
267
+ * Returns the function's identifier name, or null if anonymous.
268
+ *
269
+ * @param node - The FunctionDeclaration AST node.
270
+ * @returns The function's identifier name, or `null` for anonymous functions.
271
+ */ function getFunctionName(node) {
272
+ var _node_id;
273
+ if (((_node_id = node.id) === null || _node_id === void 0 ? void 0 : _node_id.type) === 'Identifier') {
274
+ return node.id.name;
275
+ }
276
+ return null;
277
+ }
278
+ /**
279
+ * Builds the merged set of name patterns based on rule options.
280
+ *
281
+ * @param options - The resolved rule options.
282
+ * @returns The combined default + additional regex patterns, or an empty list when name-pattern matching is disabled.
283
+ */ function buildNamePatterns(options) {
284
+ var _options_additionalNamePatterns;
285
+ if (!options.checkNamePatterns) {
286
+ return [];
287
+ }
288
+ var additional = ((_options_additionalNamePatterns = options.additionalNamePatterns) !== null && _options_additionalNamePatterns !== void 0 ? _options_additionalNamePatterns : []).map(function(source) {
289
+ return new RegExp(source);
290
+ });
291
+ return _to_consumable_array(DEFAULT_NAME_PATTERNS).concat(_to_consumable_array(additional));
292
+ }
293
+ var utilRequireNoSideEffectsRule = {
294
+ meta: {
295
+ type: 'suggestion',
296
+ fixable: 'code',
297
+ docs: {
298
+ description: 'Require @__NO_SIDE_EFFECTS__ inside the JSDoc block of factory functions so esbuild can drop unused calls during tree-shaking.',
299
+ recommended: true
300
+ },
301
+ messages: {
302
+ missingNoSideEffectsJsdoc: 'Factory function "{{name}}" is missing the `@__NO_SIDE_EFFECTS__` annotation in its JSDoc. Add it as the last tag inside the JSDoc block so esbuild can drop unused calls during tree-shaking.',
303
+ missingJsdocForFactory: 'Factory-named function "{{name}}" has no JSDoc block. Add a JSDoc block containing `@__NO_SIDE_EFFECTS__` so esbuild can drop unused calls during tree-shaking.',
304
+ missingImplAnnotationOverloaded: 'Overloaded factory function "{{name}}" needs `@__NO_SIDE_EFFECTS__` directly on its implementation — TypeScript erases overload signatures during emit, so the JSDoc tag on the first overload is dropped from the bundled JavaScript. Add a `// @__NO_SIDE_EFFECTS__` line comment immediately above the implementation declaration.'
305
+ },
306
+ schema: [
307
+ {
308
+ type: 'object',
309
+ properties: {
310
+ checkNamePatterns: {
311
+ type: 'boolean',
312
+ description: 'Also flag functions whose names match factory naming patterns, in addition to JSDoc-tag detection.'
313
+ },
314
+ additionalNamePatterns: {
315
+ type: 'array',
316
+ items: {
317
+ type: 'string'
318
+ },
319
+ description: 'Additional name pattern source strings to treat as factory signals when checkNamePatterns is true.'
320
+ }
321
+ },
322
+ additionalProperties: false
323
+ }
324
+ ]
325
+ },
326
+ create: function create(context) {
327
+ var _context_options_;
328
+ var options = (_context_options_ = context.options[0]) !== null && _context_options_ !== void 0 ? _context_options_ : {};
329
+ var namePatterns = buildNamePatterns(options);
330
+ var sourceCode = context.sourceCode;
331
+ var sourceText = sourceCode.getText();
332
+ function nameMatchesFactoryPattern(name) {
333
+ return namePatterns.some(function(pattern) {
334
+ return pattern.test(name);
335
+ });
336
+ }
337
+ function checkFunction(node) {
338
+ var _jsdoc_text;
339
+ // Skip overload signatures (TSDeclareFunction) and bodyless declarations.
340
+ if (node.type !== 'FunctionDeclaration' || !node.body) {
341
+ return;
342
+ }
343
+ var name = getFunctionName(node);
344
+ if (!name) {
345
+ return;
346
+ }
347
+ // Walks the overload chain (if any) so we read the JSDoc on the first overload,
348
+ // any orphan annotations placed between overloads, and the (preserved) impl-leading annotation.
349
+ var _findFunctionLeadingContext = findFunctionLeadingContext(sourceCode, node), jsdoc = _findFunctionLeadingContext.jsdoc, redundantLineComments = _findFunctionLeadingContext.orphanLineComments, hasOverloads = _findFunctionLeadingContext.hasOverloads, implLineComment = _findFunctionLeadingContext.implLineComment, implHasSurvivingAnnotation = _findFunctionLeadingContext.implHasSurvivingAnnotation, chainStartStatement = _findFunctionLeadingContext.chainStartStatement;
350
+ var taggedAsFactory = (jsdoc === null || jsdoc === void 0 ? void 0 : (_jsdoc_text = jsdoc.text) === null || _jsdoc_text === void 0 ? void 0 : _jsdoc_text.includes(FACTORY_JSDOC_TAG)) === true;
351
+ var matchedByName = !taggedAsFactory && namePatterns.length > 0 && nameMatchesFactoryPattern(name);
352
+ if (!taggedAsFactory && !matchedByName) {
353
+ return;
354
+ }
355
+ // The bundled implementation already carries the marker — passing. This covers:
356
+ // - non-overloaded with the tag in the (only) JSDoc, AND
357
+
358
+ if (implHasSurvivingAnnotation) {
359
+ return;
360
+ }
361
+ // Choose the most specific message:
362
+ // - overloaded + JSDoc with the tag on first overload but no impl annotation → impl-specific.
363
+ // - has any JSDoc → JSDoc tag missing.
364
+ // - no JSDoc → JSDoc must be created.
365
+ var messageId;
366
+ if (hasOverloads && (jsdoc === null || jsdoc === void 0 ? void 0 : jsdoc.hasNoSideEffects)) {
367
+ messageId = 'missingImplAnnotationOverloaded';
368
+ } else if (jsdoc) {
369
+ messageId = 'missingNoSideEffectsJsdoc';
370
+ } else {
371
+ messageId = 'missingJsdocForFactory';
372
+ }
373
+ context.report({
374
+ node: node.id,
375
+ messageId: messageId,
376
+ data: {
377
+ name: name
378
+ },
379
+ fix: function fix(fixer) {
380
+ var fixes = [];
381
+ // Add the JSDoc tag (or create the JSDoc) so consumer-facing docs reflect the marker.
382
+ // Skipped when the existing JSDoc already carries the tag — only the impl-line comment
383
+ // would be missing in that case (handled below).
384
+ if (jsdoc && !jsdoc.hasNoSideEffects) {
385
+ var jsdocText = jsdoc.text; // text excludes /* and */
386
+ var jsdocStart = jsdoc.node.range[0];
387
+ var jsdocEnd = jsdoc.node.range[1];
388
+ // Determine the column the JSDoc starts at to align the new line.
389
+ var jsdocIndent = getLineIndent(sourceText, jsdocStart);
390
+ // If the JSDoc is single-line (e.g. `/** @dbxUtilKind factory */`),
391
+ // expand it to multi-line. Detect by absence of newline in the body.
392
+ if (!jsdocText.includes('\n')) {
393
+ var bodyTrimmed = jsdocText.replace(/^\*\s*/, '').replace(/\s*$/, '');
394
+ var newBody = "/**\n".concat(jsdocIndent, " * ").concat(bodyTrimmed, "\n").concat(jsdocIndent, " * ").concat(NO_SIDE_EFFECTS_TAG, "\n").concat(jsdocIndent, " */");
395
+ fixes.push(fixer.replaceTextRange([
396
+ jsdocStart,
397
+ jsdocEnd
398
+ ], newBody));
399
+ } else {
400
+
401
+ // immediately before the line containing the closing `*/`, so the closing line
402
+ // and existing body lines remain untouched.
403
+ var closingMarkerStart = jsdocEnd - 2; // start of `*/`
404
+ var closingLineStart = closingMarkerStart;
405
+ while(closingLineStart > 0 && sourceText.charAt(closingLineStart - 1) !== '\n'){
406
+ closingLineStart -= 1;
407
+ }
408
+ var insertion = "".concat(jsdocIndent, " * ").concat(NO_SIDE_EFFECTS_TAG, "\n");
409
+ fixes.push(fixer.insertTextBeforeRange([
410
+ closingLineStart,
411
+ closingLineStart
412
+ ], insertion));
413
+ }
414
+ } else if (!jsdoc) {
415
+ // No JSDoc anywhere — create one above the FIRST statement in the chain. For overloaded
416
+ // functions the canonical doc placement is on the first overload, not the implementation.
417
+ var nodeStart = chainStartStatement.range[0];
418
+ var indent = getLineIndent(sourceText, nodeStart);
419
+ var newJsdoc = "/**\n".concat(indent, " * @dbxUtilKind factory\n").concat(indent, " * ").concat(NO_SIDE_EFFECTS_TAG, "\n").concat(indent, " */\n").concat(indent);
420
+ fixes.push(fixer.insertTextBeforeRange([
421
+ nodeStart,
422
+ nodeStart
423
+ ], newJsdoc));
424
+ }
425
+
426
+ // implementation so the marker survives TypeScript's overload-signature erasure and reaches
427
+ // the bundled JavaScript. Skipped when one is already in place.
428
+ if (hasOverloads && !implLineComment) {
429
+ var implAnchor = getStatementAnchor(node);
430
+ var implStart = implAnchor.range[0];
431
+ var indent1 = getLineIndent(sourceText, implStart);
432
+ fixes.push(fixer.insertTextBeforeRange([
433
+ implStart,
434
+ implStart
435
+ ], "// ".concat(NO_SIDE_EFFECTS_TAG, "\n").concat(indent1)));
436
+ }
437
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
438
+ try {
439
+ // Remove any redundant adjacent annotation comments now that JSDoc carries the tag.
440
+ for(var _iterator = redundantLineComments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
441
+ var redundant = _step.value;
442
+ var _redundant_range = _sliced_to_array$1(redundant.range, 2), start = _redundant_range[0], end = _redundant_range[1];
443
+ // Extend the removal to include the trailing newline + indent so we don't leave a blank line.
444
+ var removeEnd = end;
445
+ while(removeEnd < sourceText.length && (sourceText.charAt(removeEnd) === ' ' || sourceText.charAt(removeEnd) === '\t')){
446
+ removeEnd += 1;
447
+ }
448
+ if (sourceText.charAt(removeEnd) === '\n') {
449
+ removeEnd += 1;
450
+ }
451
+ // Also drop leading indent on the comment's line.
452
+ var removeStart = start;
453
+ while(removeStart > 0 && (sourceText.charAt(removeStart - 1) === ' ' || sourceText.charAt(removeStart - 1) === '\t')){
454
+ removeStart -= 1;
455
+ }
456
+ fixes.push(fixer.removeRange([
457
+ removeStart,
458
+ removeEnd
459
+ ]));
460
+ }
461
+ } catch (err) {
462
+ _didIteratorError = true;
463
+ _iteratorError = err;
464
+ } finally{
465
+ try {
466
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
467
+ _iterator.return();
468
+ }
469
+ } finally{
470
+ if (_didIteratorError) {
471
+ throw _iteratorError;
472
+ }
473
+ }
474
+ }
475
+ return fixes;
476
+ }
477
+ });
478
+ }
479
+ return {
480
+ FunctionDeclaration: checkFunction
481
+ };
482
+ }
483
+ };
484
+
485
+ function _array_like_to_array(arr, len) {
486
+ if (len == null || len > arr.length) len = arr.length;
487
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
488
+ return arr2;
489
+ }
490
+ function _array_with_holes(arr) {
491
+ if (Array.isArray(arr)) return arr;
492
+ }
493
+ function _iterable_to_array_limit(arr, i) {
494
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
495
+ if (_i == null) return;
496
+ var _arr = [];
497
+ var _n = true;
498
+ var _d = false;
499
+ var _s, _e;
500
+ try {
501
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
502
+ _arr.push(_s.value);
503
+ if (i && _arr.length === i) break;
504
+ }
505
+ } catch (err) {
506
+ _d = true;
507
+ _e = err;
508
+ } finally{
509
+ try {
510
+ if (!_n && _i["return"] != null) _i["return"]();
511
+ } finally{
512
+ if (_d) throw _e;
513
+ }
514
+ }
515
+ return _arr;
516
+ }
517
+ function _non_iterable_rest() {
518
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
519
+ }
520
+ function _sliced_to_array(arr, i) {
521
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
522
+ }
523
+ function _unsupported_iterable_to_array(o, minLen) {
524
+ if (!o) return;
525
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
526
+ var n = Object.prototype.toString.call(o).slice(8, -1);
527
+ if (n === "Object" && o.constructor) n = o.constructor.name;
528
+ if (n === "Map" || n === "Set") return Array.from(n);
529
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
530
+ }
531
+ var utilPreferNoSideEffectsInJsdocRule = {
532
+ meta: {
533
+ type: 'suggestion',
534
+ fixable: 'code',
535
+ docs: {
536
+ description: "Prefer the @__NO_SIDE_EFFECTS__ annotation inside a function's JSDoc instead of a separate line comment between the JSDoc and the declaration.",
537
+ recommended: true
538
+ },
539
+ messages: {
540
+ preferJsdocPlacement: 'Move the `@__NO_SIDE_EFFECTS__` annotation into the JSDoc block of "{{name}}" (as the last tag before the closing) instead of a separate line comment.',
541
+ missingImplAnnotationOverloaded: '"{{name}}" carries `@__NO_SIDE_EFFECTS__` in its first-overload JSDoc but is overloaded — TypeScript erases overload signatures during emit, so the JSDoc tag is dropped from the bundled JavaScript. Add a `// @__NO_SIDE_EFFECTS__` line comment immediately above the implementation declaration.'
542
+ },
543
+ schema: []
544
+ },
545
+ create: function create(context) {
546
+ var sourceCode = context.sourceCode;
547
+ var sourceText = sourceCode.getText();
548
+ function checkFunction(node) {
549
+ var _node_id;
550
+ if (node.type !== 'FunctionDeclaration' || !node.body) {
551
+ return;
552
+ }
553
+ if (((_node_id = node.id) === null || _node_id === void 0 ? void 0 : _node_id.type) !== 'Identifier') {
554
+ return;
555
+ }
556
+ var name = node.id.name;
557
+ // Walks the overload chain (if any) so we find the JSDoc on the first overload,
558
+ // any orphan annotations between overloads, and the (preserved) impl-leading annotation.
559
+ var _findFunctionLeadingContext = findFunctionLeadingContext(sourceCode, node), jsdoc = _findFunctionLeadingContext.jsdoc, orphanLineComments = _findFunctionLeadingContext.orphanLineComments, implLineComment = _findFunctionLeadingContext.implLineComment, hasOverloads = _findFunctionLeadingContext.hasOverloads, implHasSurvivingAnnotation = _findFunctionLeadingContext.implHasSurvivingAnnotation;
560
+ if (!jsdoc) {
561
+ return;
562
+ }
563
+ // Three reasons to fire:
564
+ // 1. There's an annotation form (orphan OR overload-impl line comment) and the JSDoc
565
+ // doesn't yet carry the tag — the JSDoc needs the tag added (for docs/tooling).
566
+ // 2. There are orphan annotations to consolidate, regardless of JSDoc tag state.
567
+ // 3. Function is overloaded, JSDoc carries the tag, but the implementation lacks any
568
+ // surviving annotation — TS erases overload signatures during emit, so the JSDoc tag
569
+
570
+ // above the impl so the bundler still sees the hint.
571
+ // The impl line comment on overloaded functions is REQUIRED for tree-shaking and is never
572
+ // removed — it is only counted as a signal that the JSDoc should also carry the tag.
573
+ var hasAnyAnnotationSource = orphanLineComments.length > 0 || implLineComment !== null;
574
+ var needsJsdocTag = !jsdoc.hasNoSideEffects && hasAnyAnnotationSource;
575
+ var needsOrphanRemoval = orphanLineComments.length > 0;
576
+ var needsImplLineCommentForOverload = jsdoc.hasNoSideEffects && hasOverloads && !implHasSurvivingAnnotation;
577
+ if (!needsJsdocTag && !needsOrphanRemoval && !needsImplLineCommentForOverload) {
578
+ return;
579
+ }
580
+ context.report({
581
+ node: node.id,
582
+ messageId: needsImplLineCommentForOverload && !needsJsdocTag && !needsOrphanRemoval ? 'missingImplAnnotationOverloaded' : 'preferJsdocPlacement',
583
+ data: {
584
+ name: name
585
+ },
586
+ fix: function fix(fixer) {
587
+ var fixes = [];
588
+ var jsdocText = jsdoc.text;
589
+ var jsdocStart = jsdoc.node.range[0];
590
+ var jsdocEnd = jsdoc.node.range[1];
591
+ var jsdocIndent = getLineIndent(sourceText, jsdocStart);
592
+ // Insert into JSDoc only if needed (preserve idempotency and skip when only orphans need removal).
593
+ if (needsJsdocTag) {
594
+ if (!jsdocText.includes('\n')) {
595
+ // Single-line JSDoc — expand to multi-line so the new tag has its own line.
596
+ var bodyTrimmed = jsdocText.replace(/^\*\s*/, '').replace(/\s*$/, '');
597
+ var newBody = "/**\n".concat(jsdocIndent, " * ").concat(bodyTrimmed, "\n").concat(jsdocIndent, " * ").concat(NO_SIDE_EFFECTS_TAG, "\n").concat(jsdocIndent, " */");
598
+ fixes.push(fixer.replaceTextRange([
599
+ jsdocStart,
600
+ jsdocEnd
601
+ ], newBody));
602
+ } else {
603
+ // Multi-line JSDoc — insert a new tag line immediately before the closing `*/` line.
604
+ var closingMarkerStart = jsdocEnd - 2;
605
+ var closingLineStart = closingMarkerStart;
606
+ while(closingLineStart > 0 && sourceText.charAt(closingLineStart - 1) !== '\n'){
607
+ closingLineStart -= 1;
608
+ }
609
+ var insertion = "".concat(jsdocIndent, " * ").concat(NO_SIDE_EFFECTS_TAG, "\n");
610
+ fixes.push(fixer.insertTextBeforeRange([
611
+ closingLineStart,
612
+ closingLineStart
613
+ ], insertion));
614
+ }
615
+ }
616
+ // Overloaded function with no surviving impl annotation — insert the bundler-required
617
+ // line comment directly above the implementation declaration.
618
+ if (needsImplLineCommentForOverload) {
619
+ var implAnchor = getStatementAnchor(node);
620
+ var implStart = implAnchor.range[0];
621
+ var indent = getLineIndent(sourceText, implStart);
622
+ fixes.push(fixer.insertTextBeforeRange([
623
+ implStart,
624
+ implStart
625
+ ], "// ".concat(NO_SIDE_EFFECTS_TAG, "\n").concat(indent)));
626
+ }
627
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
628
+ try {
629
+ // Remove the orphan line/block comment annotations.
630
+ for(var _iterator = orphanLineComments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
631
+ var orphan = _step.value;
632
+ var _orphan_range = _sliced_to_array(orphan.range, 2), start = _orphan_range[0], end = _orphan_range[1];
633
+ var removeEnd = end;
634
+ while(removeEnd < sourceText.length && (sourceText.charAt(removeEnd) === ' ' || sourceText.charAt(removeEnd) === '\t')){
635
+ removeEnd += 1;
636
+ }
637
+ if (sourceText.charAt(removeEnd) === '\n') {
638
+ removeEnd += 1;
639
+ }
640
+ var removeStart = start;
641
+ while(removeStart > 0 && (sourceText.charAt(removeStart - 1) === ' ' || sourceText.charAt(removeStart - 1) === '\t')){
642
+ removeStart -= 1;
643
+ }
644
+ fixes.push(fixer.removeRange([
645
+ removeStart,
646
+ removeEnd
647
+ ]));
648
+ }
649
+ } catch (err) {
650
+ _didIteratorError = true;
651
+ _iteratorError = err;
652
+ } finally{
653
+ try {
654
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
655
+ _iterator.return();
656
+ }
657
+ } finally{
658
+ if (_didIteratorError) {
659
+ throw _iteratorError;
660
+ }
661
+ }
662
+ }
663
+ return fixes;
664
+ }
665
+ });
666
+ }
667
+ return {
668
+ FunctionDeclaration: checkFunction
669
+ };
670
+ }
671
+ };
672
+
673
+ /**
674
+ * ESLint plugin for @dereekb/util rules.
675
+ *
676
+ * Register as a plugin in your flat ESLint config, then enable individual rules
677
+ * under the chosen plugin prefix (e.g. 'dereekb-util/require-no-side-effects').
678
+ */ var utilEslintPlugin = {
679
+ rules: {
680
+ 'require-no-side-effects': utilRequireNoSideEffectsRule,
681
+ 'prefer-no-side-effects-in-jsdoc': utilPreferNoSideEffectsInJsdocRule
682
+ }
683
+ };
684
+
685
+ exports.utilEslintPlugin = utilEslintPlugin;
686
+ exports.utilPreferNoSideEffectsInJsdocRule = utilPreferNoSideEffectsInJsdocRule;
687
+ exports.utilRequireNoSideEffectsRule = utilRequireNoSideEffectsRule;