@pobammer-ts/small-rules 2.3.0 → 2.4.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.
Files changed (2) hide show
  1. package/dist/index.js +248 -230
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { parseSync } from "oxc-parser";
5
5
  import { existsSync, readFileSync, readdirSync } from "node:fs";
6
6
  import { ResolverFactory } from "oxc-resolver";
7
7
  import { type } from "arktype";
8
- import { minimatch } from "minimatch";
8
+ import { Minimatch, minimatch } from "minimatch";
9
9
  //#region src/rules/array-type-generic.ts
10
10
  function toGenericArrayType(typeNode, sourceCode) {
11
11
  if (typeNode.type === "TSParenthesizedType") return toGenericArrayType(typeNode.typeAnnotation, sourceCode);
@@ -113,6 +113,197 @@ function hasShadowedBinding(sourceCode, node, name) {
113
113
  return false;
114
114
  }
115
115
  //#endregion
116
+ //#region src/utilities/oxc-utilities.ts
117
+ const COMPONENT_NAME_PATTERN = /^[A-Z]/v;
118
+ const KEY_OF_NODE = new Set([
119
+ "end",
120
+ "loc",
121
+ "parent",
122
+ "range",
123
+ "start",
124
+ "type"
125
+ ]);
126
+ function isNode$1(value) {
127
+ return isRecord(value) && isStringRaw(value.type);
128
+ }
129
+ function isKeyOfNode(key) {
130
+ return KEY_OF_NODE.has(key);
131
+ }
132
+ function isComponentName(name) {
133
+ return COMPONENT_NAME_PATTERN.test(name);
134
+ }
135
+ function isVariableDeclarator(node) {
136
+ return node.type === "VariableDeclarator";
137
+ }
138
+ function getTypeAnnotationFromBinding(binding) {
139
+ return isTsTypeAnnotation(binding.typeAnnotation) ? binding.typeAnnotation : void 0;
140
+ }
141
+ function isTsTypeAnnotation(value) {
142
+ return isRecord(value) && "type" in value && value.type === "TSTypeAnnotation";
143
+ }
144
+ function isIdentifierNamed$1(node, name) {
145
+ return node.type === "Identifier" && node.name === name;
146
+ }
147
+ function isReactNamedCall(node, identifiers, reactNamespaces, name) {
148
+ if (node.callee.type === "Identifier") return identifiers.has(node.callee.name);
149
+ if (node.callee.type !== "MemberExpression" || node.callee.object.type !== "Identifier") return false;
150
+ return reactNamespaces.has(node.callee.object.name) && getMemberPropertyName(node.callee) === name;
151
+ }
152
+ function isUseMemoCall(node, memoIdentifiers, reactNamespaces) {
153
+ return isReactNamedCall(node, memoIdentifiers, reactNamespaces, "useMemo");
154
+ }
155
+ function getImportedName({ imported }) {
156
+ return imported.type === "Identifier" ? imported.name : imported.value;
157
+ }
158
+ function hasName(node) {
159
+ return node.type === "Identifier" && isStringRaw(node.name);
160
+ }
161
+ function isIdentifierName(node) {
162
+ return node.type === "Identifier";
163
+ }
164
+ function isJsxIdentifier$1(node) {
165
+ return node.type === "JSXIdentifier" && "name" in node;
166
+ }
167
+ function isJsxOpeningExpression(node) {
168
+ return node.type === "JSXOpeningElement";
169
+ }
170
+ function isImportDeclaration(node) {
171
+ return node.type === "ImportDeclaration";
172
+ }
173
+ function isStringLiteral(node) {
174
+ return node.type === "Literal" && isStringRaw(node.value);
175
+ }
176
+ function isCallExpression$1(node) {
177
+ return node.type === "CallExpression";
178
+ }
179
+ function isImportSpecifier$1(node) {
180
+ return node.type === "ImportSpecifier";
181
+ }
182
+ function isExportSpecifier(node) {
183
+ return node.type === "ExportSpecifier";
184
+ }
185
+ function isProperty(node) {
186
+ return node.type === "Property";
187
+ }
188
+ function isMemberExpression(node) {
189
+ return node.type === "MemberExpression";
190
+ }
191
+ function isAssignmentExpression(node) {
192
+ return node.type === "AssignmentExpression";
193
+ }
194
+ function isUnaryExpression(node) {
195
+ return node.type === "UnaryExpression";
196
+ }
197
+ function isBinaryExpression(node) {
198
+ return node.type === "BinaryExpression";
199
+ }
200
+ function isLogicalExpression(node) {
201
+ return node.type === "LogicalExpression";
202
+ }
203
+ function isConditionalExpression(node) {
204
+ return node.type === "ConditionalExpression";
205
+ }
206
+ function isSequenceExpression(node) {
207
+ return node.type === "SequenceExpression";
208
+ }
209
+ function isMethodDefinition(node) {
210
+ return node.type === "MethodDefinition" || node.type === "TSAbstractMethodDefinition";
211
+ }
212
+ function isPropertyDefinition(node) {
213
+ return node.type === "PropertyDefinition" || node.type === "TSAbstractPropertyDefinition";
214
+ }
215
+ function isImportDefaultSpecifier(node) {
216
+ return node.type === "ImportDefaultSpecifier";
217
+ }
218
+ function isImportNamespaceSpecifier(node) {
219
+ return node.type === "ImportNamespaceSpecifier";
220
+ }
221
+ function isVariableDeclaration(node) {
222
+ return node.type === "VariableDeclaration";
223
+ }
224
+ function isExportNamedDeclaration(node) {
225
+ return node.type === "ExportNamedDeclaration";
226
+ }
227
+ function isFunctionDeclaration(node) {
228
+ return node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
229
+ }
230
+ function isCallbackFunction(node) {
231
+ return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
232
+ }
233
+ function isAnyFunction(node) {
234
+ return node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
235
+ }
236
+ function isNamedGlobalCall(node, name) {
237
+ return isIdentifierNamed$1(node.callee, name);
238
+ }
239
+ function isClass(node) {
240
+ return node.type === "ClassDeclaration" || node.type === "ClassExpression";
241
+ }
242
+ function isTsTypeAliasDeclaration(node) {
243
+ return node.type === "TSTypeAliasDeclaration";
244
+ }
245
+ function isTsPropertySignature(node) {
246
+ return node.type === "TSPropertySignature";
247
+ }
248
+ function isLiteral(node) {
249
+ return node.type === "Literal";
250
+ }
251
+ function isArrowFunctionExpression(node) {
252
+ return node.type === "ArrowFunctionExpression";
253
+ }
254
+ function isFunction(node) {
255
+ return isFunctionDeclaration(node) || isArrowFunctionExpression(node);
256
+ }
257
+ function isTsQualifiedName(node) {
258
+ return node.type === "TSQualifiedName";
259
+ }
260
+ function isNumericLiteral(node) {
261
+ return node.type === "Literal" && isNumberRaw(node.value);
262
+ }
263
+ function isNewExpression(node) {
264
+ return node.type === "NewExpression";
265
+ }
266
+ function isArrayExpression(node) {
267
+ return node.type === "ArrayExpression";
268
+ }
269
+ function isObjectExpression(node) {
270
+ return node.type === "ObjectExpression";
271
+ }
272
+ function isObjectPattern(node) {
273
+ return node.type === "ObjectPattern";
274
+ }
275
+ function isTemplateLiteral(node) {
276
+ return node.type === "TemplateLiteral";
277
+ }
278
+ function isExpressionStatement(node) {
279
+ return node.type === "ExpressionStatement";
280
+ }
281
+ function isTsTypeAssertion(node) {
282
+ return node.type === "TSTypeAssertion";
283
+ }
284
+ function isTsAsExpression(node) {
285
+ return node.type === "TSAsExpression";
286
+ }
287
+ function isAssignmentPattern(node) {
288
+ return node.type === "AssignmentPattern";
289
+ }
290
+ function isThisExpression(node) {
291
+ return node.type === "ThisExpression";
292
+ }
293
+ function isStaticRequire(node) {
294
+ if (!isCallExpression$1(node) || node.optional) return false;
295
+ const { callee } = node;
296
+ if (!isIdentifierName(callee) || callee.name !== "require" || node.arguments.length !== 1) return false;
297
+ const [argument] = node.arguments;
298
+ return argument !== void 0 && isStringLiteral(argument);
299
+ }
300
+ function isBindingIdentifier(node) {
301
+ return node.type === "Identifier";
302
+ }
303
+ function isExpressionNode(node) {
304
+ return node.type !== "PrivateIdentifier";
305
+ }
306
+ //#endregion
116
307
  //#region src/rules/ban-instances.ts
117
308
  function getJsxAttributeName$2(name) {
118
309
  return name.type === "JSXIdentifier" ? name.name : name.name.name;
@@ -163,7 +354,7 @@ function getEnclosingFunctionScope(scope) {
163
354
  return currentScope;
164
355
  }
165
356
  function getInstanceClassName(node) {
166
- if (node.callee.type !== "Identifier" || node.callee.name !== "Instance") return void 0;
357
+ if (!isNamedGlobalCall(node, "Instance")) return void 0;
167
358
  const [firstArgument] = node.arguments;
168
359
  if (firstArgument?.type !== "Literal" || !isStringRaw(firstArgument.value)) return void 0;
169
360
  return firstArgument.value;
@@ -327,191 +518,6 @@ const banInstances = defineRule({
327
518
  }
328
519
  });
329
520
  //#endregion
330
- //#region src/utilities/oxc-utilities.ts
331
- const COMPONENT_NAME_PATTERN = /^[A-Z]/v;
332
- const KEY_OF_NODE = new Set([
333
- "end",
334
- "loc",
335
- "parent",
336
- "range",
337
- "start",
338
- "type"
339
- ]);
340
- function isNode$1(value) {
341
- return isRecord(value) && isStringRaw(value.type);
342
- }
343
- function isKeyOfNode(key) {
344
- return KEY_OF_NODE.has(key);
345
- }
346
- function isComponentName(name) {
347
- return COMPONENT_NAME_PATTERN.test(name);
348
- }
349
- function isVariableDeclarator(node) {
350
- return node.type === "VariableDeclarator";
351
- }
352
- function getTypeAnnotationFromBinding(binding) {
353
- return isTsTypeAnnotation(binding.typeAnnotation) ? binding.typeAnnotation : void 0;
354
- }
355
- function isTsTypeAnnotation(value) {
356
- return isRecord(value) && "type" in value && value.type === "TSTypeAnnotation";
357
- }
358
- function isIdentifierNamed$1(node, name) {
359
- return node.type === "Identifier" && node.name === name;
360
- }
361
- function isReactNamedCall(node, identifiers, reactNamespaces, name) {
362
- if (node.callee.type === "Identifier") return identifiers.has(node.callee.name);
363
- if (node.callee.type !== "MemberExpression" || node.callee.object.type !== "Identifier") return false;
364
- return reactNamespaces.has(node.callee.object.name) && getMemberPropertyName(node.callee) === name;
365
- }
366
- function isUseMemoCall(node, memoIdentifiers, reactNamespaces) {
367
- return isReactNamedCall(node, memoIdentifiers, reactNamespaces, "useMemo");
368
- }
369
- function getImportedName({ imported }) {
370
- return imported.type === "Identifier" ? imported.name : imported.value;
371
- }
372
- function hasName(node) {
373
- return node.type === "Identifier" && isStringRaw(node.name);
374
- }
375
- function isIdentifierName(node) {
376
- return node.type === "Identifier";
377
- }
378
- function isJsxIdentifier$1(node) {
379
- return node.type === "JSXIdentifier" && "name" in node;
380
- }
381
- function isJsxOpeningExpression(node) {
382
- return node.type === "JSXOpeningElement";
383
- }
384
- function isImportDeclaration(node) {
385
- return node.type === "ImportDeclaration";
386
- }
387
- function isStringLiteral(node) {
388
- return node.type === "Literal" && isStringRaw(node.value);
389
- }
390
- function isCallExpression$1(node) {
391
- return node.type === "CallExpression";
392
- }
393
- function isImportSpecifier$1(node) {
394
- return node.type === "ImportSpecifier";
395
- }
396
- function isExportSpecifier(node) {
397
- return node.type === "ExportSpecifier";
398
- }
399
- function isProperty(node) {
400
- return node.type === "Property";
401
- }
402
- function isMemberExpression(node) {
403
- return node.type === "MemberExpression";
404
- }
405
- function isAssignmentExpression(node) {
406
- return node.type === "AssignmentExpression";
407
- }
408
- function isUnaryExpression(node) {
409
- return node.type === "UnaryExpression";
410
- }
411
- function isBinaryExpression(node) {
412
- return node.type === "BinaryExpression";
413
- }
414
- function isLogicalExpression(node) {
415
- return node.type === "LogicalExpression";
416
- }
417
- function isConditionalExpression(node) {
418
- return node.type === "ConditionalExpression";
419
- }
420
- function isSequenceExpression(node) {
421
- return node.type === "SequenceExpression";
422
- }
423
- function isMethodDefinition(node) {
424
- return node.type === "MethodDefinition" || node.type === "TSAbstractMethodDefinition";
425
- }
426
- function isPropertyDefinition(node) {
427
- return node.type === "PropertyDefinition" || node.type === "TSAbstractPropertyDefinition";
428
- }
429
- function isImportDefaultSpecifier(node) {
430
- return node.type === "ImportDefaultSpecifier";
431
- }
432
- function isImportNamespaceSpecifier(node) {
433
- return node.type === "ImportNamespaceSpecifier";
434
- }
435
- function isVariableDeclaration(node) {
436
- return node.type === "VariableDeclaration";
437
- }
438
- function isExportNamedDeclaration(node) {
439
- return node.type === "ExportNamedDeclaration";
440
- }
441
- function isFunctionDeclaration(node) {
442
- return node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
443
- }
444
- function isCallbackFunction(node) {
445
- return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
446
- }
447
- function isClass(node) {
448
- return node.type === "ClassDeclaration" || node.type === "ClassExpression";
449
- }
450
- function isTsTypeAliasDeclaration(node) {
451
- return node.type === "TSTypeAliasDeclaration";
452
- }
453
- function isTsPropertySignature(node) {
454
- return node.type === "TSPropertySignature";
455
- }
456
- function isLiteral(node) {
457
- return node.type === "Literal";
458
- }
459
- function isArrowFunctionExpression(node) {
460
- return node.type === "ArrowFunctionExpression";
461
- }
462
- function isFunction(node) {
463
- return isFunctionDeclaration(node) || isArrowFunctionExpression(node);
464
- }
465
- function isTsQualifiedName(node) {
466
- return node.type === "TSQualifiedName";
467
- }
468
- function isNumericLiteral(node) {
469
- return node.type === "Literal" && isNumberRaw(node.value);
470
- }
471
- function isNewExpression(node) {
472
- return node.type === "NewExpression";
473
- }
474
- function isArrayExpression(node) {
475
- return node.type === "ArrayExpression";
476
- }
477
- function isObjectExpression(node) {
478
- return node.type === "ObjectExpression";
479
- }
480
- function isObjectPattern(node) {
481
- return node.type === "ObjectPattern";
482
- }
483
- function isTemplateLiteral(node) {
484
- return node.type === "TemplateLiteral";
485
- }
486
- function isExpressionStatement(node) {
487
- return node.type === "ExpressionStatement";
488
- }
489
- function isTsTypeAssertion(node) {
490
- return node.type === "TSTypeAssertion";
491
- }
492
- function isTsAsExpression(node) {
493
- return node.type === "TSAsExpression";
494
- }
495
- function isAssignmentPattern(node) {
496
- return node.type === "AssignmentPattern";
497
- }
498
- function isThisExpression(node) {
499
- return node.type === "ThisExpression";
500
- }
501
- function isStaticRequire(node) {
502
- if (!isCallExpression$1(node) || node.optional) return false;
503
- const { callee } = node;
504
- if (!isIdentifierName(callee) || callee.name !== "require" || node.arguments.length !== 1) return false;
505
- const [argument] = node.arguments;
506
- return argument !== void 0 && isStringLiteral(argument);
507
- }
508
- function isBindingIdentifier(node) {
509
- return node.type === "Identifier";
510
- }
511
- function isExpressionNode(node) {
512
- return node.type !== "PrivateIdentifier";
513
- }
514
- //#endregion
515
521
  //#region src/rules/ban-react-fc.ts
516
522
  const BANNED_FC_NAMES = new Set([
517
523
  "FC",
@@ -2598,7 +2604,7 @@ function isPromiseChainCall(node) {
2598
2604
  }
2599
2605
  function isAsyncIife({ callee }) {
2600
2606
  const unwrapped = unwrapExpression(callee);
2601
- return (unwrapped.type === "ArrowFunctionExpression" || unwrapped.type === "FunctionExpression") && unwrapped.async;
2607
+ return isCallbackFunction(unwrapped) && unwrapped.async;
2602
2608
  }
2603
2609
  function getThisAsyncMethodName({ callee }, asyncMethods) {
2604
2610
  if (callee.type !== "MemberExpression" || callee.object.type !== "ThisExpression" || callee.property.type !== "Identifier") return;
@@ -2615,7 +2621,7 @@ function getLocalVariableAssignment(node) {
2615
2621
  return parent.id.type === "Identifier" ? parent.id.name : void 0;
2616
2622
  }
2617
2623
  function isNonIifeFunction(node) {
2618
- if (node.type !== "ArrowFunctionExpression" && node.type !== "FunctionExpression") return false;
2624
+ if (!isCallbackFunction(node)) return false;
2619
2625
  if (node.parent.type === "ParenthesizedExpression" && node.parent.parent.type === "CallExpression" && node.parent.parent.callee === node.parent) return false;
2620
2626
  return node.parent.type !== "CallExpression" || node.parent.callee !== node;
2621
2627
  }
@@ -2766,7 +2772,7 @@ const noColor3Constructor = defineRule({
2766
2772
  create(context) {
2767
2773
  const options = normalizeOptions$4(context.options[0]);
2768
2774
  return { NewExpression(node) {
2769
- if (node.callee.type !== "Identifier" || node.callee.name !== "Color3") return;
2775
+ if (!isNamedGlobalCall(node, "Color3")) return;
2770
2776
  const parameters = node.arguments;
2771
2777
  if (parameters.length === 0) return;
2772
2778
  const collected = collectNumericComponents(parameters);
@@ -3446,14 +3452,6 @@ const LOOP_TYPES = new Set([
3446
3452
  function isLoopNode(node) {
3447
3453
  return LOOP_TYPES.has(node.type);
3448
3454
  }
3449
- const FUNCTION_BOUNDARY_TYPES$1 = new Set([
3450
- "ArrowFunctionExpression",
3451
- "FunctionDeclaration",
3452
- "FunctionExpression"
3453
- ]);
3454
- function isFunctionBoundary(node) {
3455
- return FUNCTION_BOUNDARY_TYPES$1.has(node.type);
3456
- }
3457
3455
  function findLabeledStatementBody$1(labelName, startingNode) {
3458
3456
  let current = startingNode;
3459
3457
  while (current !== null) {
@@ -3467,7 +3465,7 @@ function breaksTargetLoop(statement, loopNode) {
3467
3465
  if (statement.label) return findLabeledStatementBody$1(statement.label.name, statement.parent) === loopNode;
3468
3466
  let current = statement.parent;
3469
3467
  while (current !== null) {
3470
- if (current.type === "Program" || isFunctionBoundary(current) || current.type === "SwitchStatement") return false;
3468
+ if (current.type === "Program" || isAnyFunction(current) || current.type === "SwitchStatement") return false;
3471
3469
  if (isLoopNode(current)) return current === loopNode;
3472
3470
  current = current.parent;
3473
3471
  }
@@ -5534,7 +5532,7 @@ function hasAspectRatioConstraintInSubtree(node) {
5534
5532
  function getFunctionComponentName(node) {
5535
5533
  if (node.type === "FunctionDeclaration") return node.id?.name;
5536
5534
  /* v8 ignore next -- @preserve only named declarations and assigned function expressions are inspected as components. */
5537
- if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") {
5535
+ if (isCallbackFunction(node)) {
5538
5536
  const { parent } = node;
5539
5537
  /* v8 ignore next -- @preserve assigned function components have identifier variable declarator parents. */
5540
5538
  if (parent.type === "VariableDeclarator" && parent.id.type === "Identifier") return parent.id.name;
@@ -5815,7 +5813,36 @@ const isRuleOptions = type({
5815
5813
  "checkComputed?": "boolean | undefined",
5816
5814
  restrictions: isRestriction.array().readonly()
5817
5815
  }).readonly();
5818
- const MATCH_BASE = { matchBase: true };
5816
+ const FILE_MATCH_OPTIONS = { matchBase: true };
5817
+ const NAME_MATCH_OPTIONS = {
5818
+ dot: true,
5819
+ magicalBraces: true
5820
+ };
5821
+ function compileMatcher(pattern, options) {
5822
+ const matcher = new Minimatch(pattern, options);
5823
+ const hasMagic = matcher.hasMagic();
5824
+ return {
5825
+ hasMagic,
5826
+ matches: hasMagic ? (value) => matcher.match(value) : (value) => value === pattern
5827
+ };
5828
+ }
5829
+ function createPropertyMatcher(patterns) {
5830
+ const literalPatterns = /* @__PURE__ */ new Set();
5831
+ const globMatchers = [];
5832
+ for (const pattern of patterns) {
5833
+ const matcher = compileMatcher(pattern, NAME_MATCH_OPTIONS);
5834
+ if (matcher.hasMagic) globMatchers.push(matcher.matches);
5835
+ else literalPatterns.add(pattern);
5836
+ }
5837
+ return (property) => literalPatterns.has(property) || globMatchers.some((matcher) => matcher(property));
5838
+ }
5839
+ function compileRestriction(restriction) {
5840
+ return {
5841
+ matchesObject: compileMatcher(restriction.object, NAME_MATCH_OPTIONS).matches,
5842
+ matchesProperty: createPropertyMatcher(restriction.properties),
5843
+ message: restriction.message
5844
+ };
5845
+ }
5819
5846
  function getEffectiveOptions(context) {
5820
5847
  const [options] = context.options;
5821
5848
  if (!isRuleOptions.allows(options)) return {
@@ -5824,11 +5851,11 @@ function getEffectiveOptions(context) {
5824
5851
  restrictions: []
5825
5852
  };
5826
5853
  const { allowFiles, checkComputed, restrictions } = options;
5827
- const isAllowedFile = allowFiles?.some((pattern) => minimatch(context.filename, pattern, MATCH_BASE)) ?? false;
5854
+ const isAllowedFile = allowFiles?.some((pattern) => minimatch(context.filename, pattern, FILE_MATCH_OPTIONS)) ?? false;
5828
5855
  return {
5829
5856
  checkComputed: checkComputed ?? true,
5830
5857
  isAllowedFile,
5831
- restrictions
5858
+ restrictions: restrictions.map(compileRestriction)
5832
5859
  };
5833
5860
  }
5834
5861
  const noRestrictedPropertyAssignment = defineRule({
@@ -5840,8 +5867,8 @@ const noRestrictedPropertyAssignment = defineRule({
5840
5867
  const property = getMemberPropertyName(node);
5841
5868
  if (property === void 0) return;
5842
5869
  for (const restriction of restrictions) {
5843
- if (restriction.object !== node.object.name) continue;
5844
- if (restriction.properties.includes("*") || restriction.properties.includes(property)) {
5870
+ if (!restriction.matchesObject(node.object.name)) continue;
5871
+ if (restriction.matchesProperty(property)) {
5845
5872
  if (restriction.message === void 0) context.report({
5846
5873
  data: {
5847
5874
  object: node.object.name,
@@ -5992,7 +6019,7 @@ function isStaticComponentVariable(variable, name) {
5992
6019
  if (definition.node.type !== "VariableDeclarator") continue;
5993
6020
  const initializer = definition.node.init ?? void 0;
5994
6021
  if (initializer === void 0) continue;
5995
- if (initializer.type === "ArrowFunctionExpression" || initializer.type === "FunctionExpression" || initializer.type === "ClassExpression") return true;
6022
+ if (isCallbackFunction(initializer) || initializer.type === "ClassExpression") return true;
5996
6023
  }
5997
6024
  return false;
5998
6025
  }
@@ -6385,9 +6412,14 @@ function collectAllScopes(root) {
6385
6412
  function isFunctionLikeInitializer(node) {
6386
6413
  return isCallbackFunction(node) || node.type === "ClassExpression";
6387
6414
  }
6415
+ const OBJECT_LIKE_INITIALIZER_TYPES = new Set([
6416
+ "ArrayExpression",
6417
+ "ObjectExpression",
6418
+ "JSXElement",
6419
+ "JSXFragment"
6420
+ ]);
6388
6421
  function isObjectLikeInitializer(initializer, patterns, sourceCode) {
6389
- if (initializer.type === "ArrayExpression" || initializer.type === "ObjectExpression") return true;
6390
- if (initializer.type === "JSXElement" || initializer.type === "JSXFragment") return true;
6422
+ if (OBJECT_LIKE_INITIALIZER_TYPES.has(initializer.type)) return true;
6391
6423
  if (initializer.type !== "CallExpression" && initializer.type !== "NewExpression") return false;
6392
6424
  const candidateText = sourceCode.getText(initializer.callee);
6393
6425
  for (const pattern of patterns) if (pattern.test(candidateText)) return true;
@@ -22045,7 +22077,7 @@ function getSequenceKeypointName(sequenceName) {
22045
22077
  }
22046
22078
  function getKeypointValue(node, keypointName, time) {
22047
22079
  if (node.type !== "NewExpression") return void 0;
22048
- if (node.callee.type !== "Identifier" || node.callee.name !== keypointName) return void 0;
22080
+ if (!isNamedGlobalCall(node, keypointName)) return void 0;
22049
22081
  if (node.arguments.length !== 2) return void 0;
22050
22082
  const [timeArgument, valueArgument] = node.arguments;
22051
22083
  /* v8 ignore next -- @preserve length was checked above; this only guards malformed AST tuples. */
@@ -22704,7 +22736,7 @@ function collectArguments(parameters) {
22704
22736
  const preferUDim2Shorthand = defineRule({
22705
22737
  create(context) {
22706
22738
  return { NewExpression(node) {
22707
- if (node.callee.type !== "Identifier" || node.callee.name !== "UDim2") return;
22739
+ if (!isNamedGlobalCall(node, "UDim2")) return;
22708
22740
  const collected = collectArguments(node.arguments);
22709
22741
  if (collected === void 0) return;
22710
22742
  const [scaleXNode, offsetXNode, scaleYNode, offsetYNode] = node.arguments;
@@ -25200,7 +25232,6 @@ const EMPTY_CALLBACK_USAGE = {
25200
25232
  memoization: false
25201
25233
  };
25202
25234
  const SHOULD_ASCEND_TYPES = new Set(["ConditionalExpression", "LogicalExpression"]);
25203
- const IS_FUNCTION_EXPRESSION = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
25204
25235
  const CONTROL_FLOW_TYPES = new Set([
25205
25236
  "BlockStatement",
25206
25237
  "CatchClause",
@@ -25225,13 +25256,10 @@ function ascendPastWrappers(node) {
25225
25256
  while (current !== void 0 && WRAPPER_PARENT_TYPES.has(current.type)) current = getParent(current);
25226
25257
  return current;
25227
25258
  }
25228
- function isFunctionLikeNode(node) {
25229
- return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration";
25230
- }
25231
25259
  function getEnclosingFunctionLike(node) {
25232
25260
  let current = getParent(node);
25233
25261
  while (current !== void 0) {
25234
- if (isFunctionLikeNode(current)) return current;
25262
+ if (isAnyFunction(current)) return current;
25235
25263
  current = getParent(current);
25236
25264
  }
25237
25265
  }
@@ -25338,7 +25366,7 @@ function isFunctionReturnStatement(parent) {
25338
25366
  while (currentNode !== void 0 && CONTROL_FLOW_TYPES.has(currentNode.type)) currentNode = ascendPastWrappers(getParent(currentNode));
25339
25367
  /* v8 ignore next -- @preserve return statements that contain JSX are parser-nested inside a function body. */
25340
25368
  if (currentNode === void 0) return false;
25341
- return IS_FUNCTION_EXPRESSION.has(currentNode.type) || currentNode.type === "FunctionDeclaration";
25369
+ return isAnyFunction(currentNode);
25342
25370
  }
25343
25371
  function isTopLevelReturn(node) {
25344
25372
  if (!isTopLevelFunctionReturn(node)) return false;
@@ -26118,11 +26146,6 @@ const strictComponentBoundaries = defineRule({
26118
26146
  });
26119
26147
  //#endregion
26120
26148
  //#region src/rules/use-exhaustive-dependencies.ts
26121
- const FUNCTION_DECLARATIONS = new Set([
26122
- "ArrowFunctionExpression",
26123
- "FunctionDeclaration",
26124
- "FunctionExpression"
26125
- ]);
26126
26149
  const UNSTABLE_VALUES = new Set([
26127
26150
  "ArrayExpression",
26128
26151
  "ArrowFunctionExpression",
@@ -26431,7 +26454,7 @@ function isDeclaredInComponentBody(variable, closureNode) {
26431
26454
  /* v8 ignore next -- @preserve hook closure nodes are nested inside their containing component function. */
26432
26455
  let parent = closureNode.parent ?? void 0;
26433
26456
  while (parent) {
26434
- if (FUNCTION_DECLARATIONS.has(parent.type)) {
26457
+ if (isAnyFunction(parent)) {
26435
26458
  const functionParent = parent;
26436
26459
  if (variable.defs.some((definition) => {
26437
26460
  if (definition.type !== "Parameter") return false;
@@ -26627,7 +26650,7 @@ function reportUnnecessaryDependency(context, dependencies, dependency, dependen
26627
26650
  });
26628
26651
  }
26629
26652
  function isCallbackFunctionNode(node) {
26630
- return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression" || node?.type === "FunctionDeclaration";
26653
+ return node !== void 0 && isAnyFunction(node);
26631
26654
  }
26632
26655
  function getRequiredCaptures(captures, stableHooks) {
26633
26656
  return captures.filter((capture) => capture.forceDependency || !isStableValue(capture.variable, capture.name, stableHooks));
@@ -26794,7 +26817,7 @@ const useExhaustiveDependencies = defineRule({
26794
26817
  }
26795
26818
  function resolveClosureFunction(closureArgument, callExpression) {
26796
26819
  if (closureArgument.type === "ArrowFunctionExpression") return closureArgument;
26797
- if (!(FUNCTION_DECLARATIONS.has(closureArgument.type) || closureArgument.type === "Identifier")) return void 0;
26820
+ if (!(isAnyFunction(closureArgument) || closureArgument.type === "Identifier")) return void 0;
26798
26821
  const resolved = resolveFunctionReference(closureArgument, getScope(callExpression));
26799
26822
  return isCallbackFunctionNode(resolved) ? resolved : void 0;
26800
26823
  }
@@ -26927,17 +26950,12 @@ function isHookCall(node) {
26927
26950
  const hookName = getHookName$1(node);
26928
26951
  return hookName !== void 0 && isReactHookName(hookName);
26929
26952
  }
26930
- const FUNCTION_BOUNDARIES = new Set([
26931
- "ArrowFunctionExpression",
26932
- "FunctionDeclaration",
26933
- "FunctionExpression"
26934
- ]);
26935
26953
  function isInFinallyBlock(node) {
26936
26954
  let current = node.parent;
26937
26955
  const maxDepth = 20;
26938
26956
  let inFinallyBlock = false;
26939
26957
  for (let depth = 0; depth < maxDepth && current !== null; depth += 1) {
26940
- if (FUNCTION_BOUNDARIES.has(current.type)) break;
26958
+ if (isAnyFunction(current)) break;
26941
26959
  if (current.type === "TryStatement") {
26942
26960
  let checkNode = node;
26943
26961
  while (checkNode !== null && checkNode !== current) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pobammer-ts/small-rules",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Various Oxlint-native rules for linting roblox-ts projects.",
5
5
  "keywords": [
6
6
  "lint",