@blumintinc/eslint-plugin-blumint 1.8.1 → 1.9.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.
@@ -104,10 +104,15 @@ const containsJsxInFunction = (node) => {
104
104
  };
105
105
  /**
106
106
  * Checks if an expression contains JSX elements
107
+ *
108
+ * This function is used to determine if a useMemo hook is returning JSX directly.
109
+ * We want to avoid flagging useMemo hooks that return data structures that happen to contain JSX,
110
+ * as these are typically used for configuration or data preparation, not for rendering components.
107
111
  */
108
112
  const containsJsxInExpression = (node) => {
109
113
  if (!node)
110
114
  return false;
115
+ // Direct JSX element or fragment
111
116
  if (isJsxElement(node)) {
112
117
  return true;
113
118
  }
@@ -117,8 +122,63 @@ const containsJsxInExpression = (node) => {
117
122
  case utils_1.AST_NODE_TYPES.LogicalExpression:
118
123
  return containsJsxInExpression(node.left) || containsJsxInExpression(node.right);
119
124
  case utils_1.AST_NODE_TYPES.ObjectExpression:
125
+ // Special case: If this is an object with both JSX and non-JSX properties,
126
+ // it's likely a data structure that happens to contain JSX, not a component
127
+ let hasNonJsxProperties = false;
128
+ let hasJsxProperties = false;
129
+ for (const property of node.properties) {
130
+ if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
131
+ if (isJsxElement(property.value)) {
132
+ hasJsxProperties = true;
133
+ }
134
+ else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
135
+ hasNonJsxProperties = true;
136
+ }
137
+ }
138
+ }
139
+ // If the object has both JSX and non-JSX properties, it's likely a data object
140
+ // that happens to contain JSX, not a component that should be extracted
141
+ if (hasNonJsxProperties && hasJsxProperties) {
142
+ return false;
143
+ }
144
+ // If it only has JSX properties, it might be a collection of components
145
+ if (hasJsxProperties && !hasNonJsxProperties) {
146
+ return true;
147
+ }
120
148
  return containsJsxInObject(node);
121
149
  case utils_1.AST_NODE_TYPES.CallExpression:
150
+ // Special case for array methods that return data structures with JSX
151
+ if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
152
+ node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
153
+ node.callee.property.name === 'map' &&
154
+ node.arguments.length > 0) {
155
+ const callback = node.arguments[0];
156
+ if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
157
+ callback.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
158
+ // Check if the callback returns an object with both JSX and non-JSX properties
159
+ if (callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement &&
160
+ callback.body.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
161
+ let hasNonJsxProperties = false;
162
+ let hasJsxProperties = false;
163
+ for (const property of callback.body.properties) {
164
+ if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
165
+ if (isJsxElement(property.value)) {
166
+ hasJsxProperties = true;
167
+ }
168
+ else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
169
+ hasNonJsxProperties = true;
170
+ }
171
+ }
172
+ }
173
+ // If the object has both JSX and non-JSX properties, it's likely a data object
174
+ if (hasNonJsxProperties && hasJsxProperties) {
175
+ return false;
176
+ }
177
+ }
178
+ // For other cases, check if the function returns JSX directly
179
+ return containsJsxInFunction(callback);
180
+ }
181
+ }
122
182
  // Check if it's an IIFE
123
183
  if (node.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
124
184
  node.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
@@ -127,7 +187,7 @@ const containsJsxInExpression = (node) => {
127
187
  // Check array methods
128
188
  if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
129
189
  node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
130
- const arrayMethods = ['map', 'filter', 'find', 'findIndex', 'some', 'every', 'reduce'];
190
+ const arrayMethods = ['filter', 'find', 'findIndex', 'some', 'every', 'reduce'];
131
191
  if (arrayMethods.includes(node.callee.property.name) && node.arguments.length > 0) {
132
192
  const callback = node.arguments[0];
133
193
  if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
@@ -224,6 +284,9 @@ const containsJsxInBlockStatement = (node) => {
224
284
  * This function distinguishes between:
225
285
  * 1. useMemo returning JSX directly (invalid)
226
286
  * 2. useMemo returning an object that contains JSX properties (valid)
287
+ * 3. useMemo returning non-JSX values like numbers, strings, etc. (valid)
288
+ *
289
+ * The rule should only fire when useMemo explicitly returns ReactNode from JSX.
227
290
  */
228
291
  const containsJsxInUseMemo = (node) => {
229
292
  if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
@@ -232,362 +295,26 @@ const containsJsxInUseMemo = (node) => {
232
295
  const callback = node.arguments[0];
233
296
  if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
234
297
  callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
235
- // Special case handling for the test cases
236
- // Check for specific patterns in the code that match the failing test cases
237
- // Check for the pattern: data.map(renderItem) where renderItem is a function that returns JSX
298
+ // For block statements, we need to check if any return statement contains JSX
238
299
  if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
239
- let hasRenderItemFunction = false;
240
- let returnsMapWithRenderItem = false;
300
+ // Check each return statement to see if it returns JSX
241
301
  for (const statement of callback.body.body) {
242
- // Check for a function named renderItem that returns JSX
243
- if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
244
- for (const declarator of statement.declarations) {
245
- if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier &&
246
- declarator.id.name === 'renderItem' &&
247
- declarator.init &&
248
- (declarator.init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
249
- declarator.init.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
250
- const func = declarator.init;
251
- if (func.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
252
- for (const funcStmt of func.body.body) {
253
- if (funcStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
254
- funcStmt.argument &&
255
- isJsxElement(funcStmt.argument)) {
256
- hasRenderItemFunction = true;
257
- }
258
- }
259
- }
260
- else if (isJsxElement(func.body)) {
261
- hasRenderItemFunction = true;
262
- }
263
- }
264
- }
265
- }
266
- // Check for return data.map(renderItem)
267
302
  if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
268
303
  statement.argument &&
269
- statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression &&
270
- statement.argument.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
271
- statement.argument.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
272
- statement.argument.callee.property.name === 'map') {
273
- if (statement.argument.arguments.length > 0 &&
274
- statement.argument.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier &&
275
- statement.argument.arguments[0].name === 'renderItem') {
276
- returnsMapWithRenderItem = true;
277
- }
278
- }
279
- }
280
- if (hasRenderItemFunction && returnsMapWithRenderItem) {
281
- return true;
282
- }
283
- // Check for if/else if/else statements that return JSX
284
- let hasIfStatementsReturningJsx = false;
285
- for (const statement of callback.body.body) {
286
- if (statement.type === utils_1.AST_NODE_TYPES.IfStatement) {
287
- // Check if the if statement returns JSX
288
- if (statement.consequent.type === utils_1.AST_NODE_TYPES.BlockStatement) {
289
- for (const ifStmt of statement.consequent.body) {
290
- if (ifStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
291
- ifStmt.argument &&
292
- isJsxElement(ifStmt.argument)) {
293
- hasIfStatementsReturningJsx = true;
294
- }
295
- }
296
- }
297
- else if (statement.consequent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
298
- statement.consequent.argument &&
299
- isJsxElement(statement.consequent.argument)) {
300
- hasIfStatementsReturningJsx = true;
301
- }
302
- // Check if the else clause returns JSX
303
- if (statement.alternate) {
304
- if (statement.alternate.type === utils_1.AST_NODE_TYPES.BlockStatement) {
305
- for (const elseStmt of statement.alternate.body) {
306
- if (elseStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
307
- elseStmt.argument &&
308
- isJsxElement(elseStmt.argument)) {
309
- hasIfStatementsReturningJsx = true;
310
- }
311
- }
312
- }
313
- else if (statement.alternate.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
314
- statement.alternate.argument &&
315
- isJsxElement(statement.alternate.argument)) {
316
- hasIfStatementsReturningJsx = true;
317
- }
318
- else if (statement.alternate.type === utils_1.AST_NODE_TYPES.IfStatement) {
319
- // Handle else if
320
- if (statement.alternate.consequent.type === utils_1.AST_NODE_TYPES.BlockStatement) {
321
- for (const elseIfStmt of statement.alternate.consequent.body) {
322
- if (elseIfStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
323
- elseIfStmt.argument &&
324
- isJsxElement(elseIfStmt.argument)) {
325
- hasIfStatementsReturningJsx = true;
326
- }
327
- }
328
- }
329
- else if (statement.alternate.consequent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
330
- statement.alternate.consequent.argument &&
331
- isJsxElement(statement.alternate.consequent.argument)) {
332
- hasIfStatementsReturningJsx = true;
333
- }
334
- }
335
- }
336
- }
337
- }
338
- if (hasIfStatementsReturningJsx) {
339
- return true;
340
- }
341
- // Check for components object with JSX properties
342
- let hasComponentsObject = false;
343
- let returnsComponentsProperty = false;
344
- for (const statement of callback.body.body) {
345
- // Check for const components = { ... } with JSX properties
346
- if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
347
- for (const declarator of statement.declarations) {
348
- if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier &&
349
- declarator.id.name === 'components' &&
350
- declarator.init &&
351
- declarator.init.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
352
- for (const property of declarator.init.properties) {
353
- if (property.type === utils_1.AST_NODE_TYPES.Property &&
354
- property.value &&
355
- isJsxElement(property.value)) {
356
- hasComponentsObject = true;
357
- break;
358
- }
359
- }
360
- }
361
- }
362
- }
363
- // Check for return components[componentType] || <div>...</div>
364
- if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
365
- statement.argument) {
366
- if (statement.argument.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
367
- statement.argument.operator === '||' &&
368
- statement.argument.left.type === utils_1.AST_NODE_TYPES.MemberExpression &&
369
- statement.argument.left.object.type === utils_1.AST_NODE_TYPES.Identifier &&
370
- statement.argument.left.object.name === 'components') {
371
- returnsComponentsProperty = true;
372
- }
373
- else if (statement.argument.type === utils_1.AST_NODE_TYPES.MemberExpression &&
374
- statement.argument.object.type === utils_1.AST_NODE_TYPES.Identifier &&
375
- statement.argument.object.name === 'components') {
376
- returnsComponentsProperty = true;
377
- }
304
+ isJsxElement(statement.argument)) {
305
+ return true;
378
306
  }
379
307
  }
380
- if (hasComponentsObject && returnsComponentsProperty) {
381
- return true;
382
- }
308
+ // If we didn't find any return statements with JSX, check for more complex patterns
309
+ return containsJsxInBlockStatement(callback.body);
383
310
  }
384
- // Direct JSX return (arrow function with expression body)
385
- if (callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
386
- // Direct JSX element
311
+ else {
312
+ // Direct JSX element - this is the primary case we want to catch
387
313
  if (isJsxElement(callback.body)) {
388
314
  return true;
389
315
  }
390
- // Array methods returning JSX
391
- if (callback.body.type === utils_1.AST_NODE_TYPES.CallExpression &&
392
- callback.body.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
393
- callback.body.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
394
- ['map', 'filter', 'find'].includes(callback.body.callee.property.name) &&
395
- callback.body.arguments.length > 0) {
396
- const mapCallback = callback.body.arguments[0];
397
- if ((mapCallback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
398
- mapCallback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
399
- isJsxElement(mapCallback.body)) {
400
- return true;
401
- }
402
- }
403
- // Conditional expression with JSX
404
- if (callback.body.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
405
- (isJsxElement(callback.body.consequent) || isJsxElement(callback.body.alternate))) {
406
- return true;
407
- }
408
- // Don't flag objects that contain JSX properties and are returned directly
409
- // This is the key fix for the bug
410
- if (callback.body.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
411
- // Check if this is a pure data object or if it's being used to store JSX for later use
412
- let hasNonJsxProperties = false;
413
- let hasJsxProperties = false;
414
- for (const property of callback.body.properties) {
415
- if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
416
- if (isJsxElement(property.value)) {
417
- hasJsxProperties = true;
418
- }
419
- else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
420
- hasNonJsxProperties = true;
421
- }
422
- }
423
- }
424
- // If the object has both JSX and non-JSX properties, it's likely a data object
425
- // that happens to contain JSX, not a component that should be extracted
426
- if (hasNonJsxProperties && hasJsxProperties) {
427
- return false;
428
- }
429
- // If it only has JSX properties, it might be a collection of components
430
- // that should be extracted
431
- if (hasJsxProperties && !hasNonJsxProperties) {
432
- return true;
433
- }
434
- return false;
435
- }
436
- // Member expression that might return JSX
437
- if (callback.body.type === utils_1.AST_NODE_TYPES.MemberExpression) {
438
- // We need to check if this is accessing a property of an object that contains JSX
439
- // This is a complex case, but we'll assume it's returning JSX
440
- return true;
441
- }
442
- }
443
- // For block statements, we need to analyze the return statements
444
- if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
445
- // Check for variable declarations that might be used to return JSX
446
- const jsxVariables = new Set();
447
- const objectVariables = new Set();
448
- const functionVariables = new Set();
449
- // First pass: collect information about variables
450
- for (const statement of callback.body.body) {
451
- if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
452
- for (const declarator of statement.declarations) {
453
- if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier && declarator.init) {
454
- const varName = declarator.id.name;
455
- // Check for JSX assignments
456
- if (isJsxElement(declarator.init)) {
457
- jsxVariables.add(varName);
458
- }
459
- // Check for object assignments
460
- if (declarator.init.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
461
- let hasJsxProperty = false;
462
- for (const property of declarator.init.properties) {
463
- if (property.type === utils_1.AST_NODE_TYPES.Property && property.value && isJsxElement(property.value)) {
464
- hasJsxProperty = true;
465
- break;
466
- }
467
- }
468
- if (hasJsxProperty) {
469
- objectVariables.add(varName);
470
- }
471
- }
472
- // Check for function assignments
473
- if (declarator.init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
474
- declarator.init.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
475
- if (containsJsxInFunction(declarator.init)) {
476
- functionVariables.add(varName);
477
- }
478
- }
479
- }
480
- }
481
- }
482
- }
483
- // Second pass: check return statements
484
- for (const statement of callback.body.body) {
485
- if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
486
- // Direct JSX return
487
- if (isJsxElement(statement.argument)) {
488
- return true;
489
- }
490
- // Return of a JSX variable
491
- if (statement.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
492
- jsxVariables.has(statement.argument.name)) {
493
- return true;
494
- }
495
- // Return of a function call that might return JSX
496
- if (statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
497
- // Check for array methods
498
- if (statement.argument.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
499
- statement.argument.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
500
- ['map', 'filter', 'find'].includes(statement.argument.callee.property.name) &&
501
- statement.argument.arguments.length > 0) {
502
- const mapCallback = statement.argument.arguments[0];
503
- if ((mapCallback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
504
- mapCallback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
505
- (isJsxElement(mapCallback.body) ||
506
- (mapCallback.body.type === utils_1.AST_NODE_TYPES.BlockStatement &&
507
- mapCallback.body.body.some(stmt => stmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
508
- stmt.argument &&
509
- isJsxElement(stmt.argument))))) {
510
- return true;
511
- }
512
- // Check for array.map(renderItem) where renderItem is a variable
513
- if (statement.argument.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier &&
514
- functionVariables.has(statement.argument.arguments[0].name)) {
515
- return true;
516
- }
517
- }
518
- // Check for IIFE
519
- if ((statement.argument.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
520
- statement.argument.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
521
- containsJsxInFunction(statement.argument.callee)) {
522
- return true;
523
- }
524
- // Check for function calls that might return JSX
525
- if (statement.argument.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
526
- // Check if we're calling a function that returns JSX
527
- if (functionVariables.has(statement.argument.callee.name)) {
528
- return true;
529
- }
530
- // Check if we're calling a function defined in the useMemo
531
- for (const innerStatement of callback.body.body) {
532
- if (innerStatement.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
533
- innerStatement.id &&
534
- innerStatement.id.name === statement.argument.callee.name) {
535
- // Check if this function returns JSX
536
- if (containsJsxInFunction(innerStatement)) {
537
- return true;
538
- }
539
- }
540
- }
541
- }
542
- }
543
- // Return of a conditional expression with JSX
544
- if (statement.argument.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
545
- (isJsxElement(statement.argument.consequent) || isJsxElement(statement.argument.alternate))) {
546
- return true;
547
- }
548
- // Return of an object property that might be JSX
549
- if (statement.argument.type === utils_1.AST_NODE_TYPES.MemberExpression) {
550
- // Check if we're accessing a property of an object that contains JSX
551
- if (statement.argument.object.type === utils_1.AST_NODE_TYPES.Identifier &&
552
- objectVariables.has(statement.argument.object.name)) {
553
- return true;
554
- }
555
- // If we can't determine, assume it might return JSX
556
- return true;
557
- }
558
- // Return of an object with JSX properties
559
- if (statement.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
560
- // Check if this is a pure data object or if it's being used to store JSX
561
- let hasNonJsxProperties = false;
562
- let hasJsxProperties = false;
563
- for (const property of statement.argument.properties) {
564
- if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
565
- if (isJsxElement(property.value)) {
566
- hasJsxProperties = true;
567
- }
568
- else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
569
- hasNonJsxProperties = true;
570
- }
571
- }
572
- }
573
- // If the object has both JSX and non-JSX properties, it's likely a data object
574
- // that happens to contain JSX, not a component that should be extracted
575
- if (hasNonJsxProperties && hasJsxProperties) {
576
- continue;
577
- }
578
- // If it only has JSX properties, it might be a collection of components
579
- // that should be extracted
580
- if (hasJsxProperties && !hasNonJsxProperties) {
581
- return true;
582
- }
583
- }
584
- }
585
- // Check switch statements in the block
586
- if (statement.type === utils_1.AST_NODE_TYPES.SwitchStatement &&
587
- containsJsxInSwitchStatement(statement)) {
588
- return true;
589
- }
590
- }
316
+ // For non-JSX expressions, we need to check if they contain JSX
317
+ return containsJsxInExpression(callback.body);
591
318
  }
592
319
  }
593
320
  }
@@ -598,7 +325,7 @@ exports.reactUseMemoShouldBeComponent = (0, createRule_1.createRule)({
598
325
  meta: {
599
326
  type: 'suggestion',
600
327
  docs: {
601
- description: 'Enforce that useMemo hooks returning React nodes should be abstracted into separate React components',
328
+ description: 'Enforce that useMemo hooks explicitly returning JSX should be abstracted into separate React components',
602
329
  recommended: 'error',
603
330
  },
604
331
  schema: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -45,7 +45,7 @@
45
45
  "commitlint": "commitlint --edit"
46
46
  },
47
47
  "dependencies": {
48
- "@types/pluralize": "^0.0.33",
48
+ "@types/pluralize": "0.0.33",
49
49
  "compromise": "14.14.4",
50
50
  "minimatch": "10.0.1",
51
51
  "pluralize": "8.0.0",
@@ -85,7 +85,7 @@
85
85
  "fs": "0.0.1-security",
86
86
  "husky": "9.1.7",
87
87
  "jest": "29.7.0",
88
- "jest-junit": "^14.0.0",
88
+ "jest-junit": "14.0.0",
89
89
  "jsonc-eslint-parser": "2.3.0",
90
90
  "npm-run-all": "4.1.5",
91
91
  "prettier": "2.7.1",
@@ -93,9 +93,9 @@
93
93
  "remark-lint": "9.1.1",
94
94
  "remark-preset-lint-recommended": "6.1.2",
95
95
  "semantic-release": "19.0.3",
96
- "ts-jest": "^29.2.6",
96
+ "ts-jest": "29.2.6",
97
97
  "ts-node": "10.9.1",
98
- "typescript": "^4.9.5"
98
+ "typescript": "4.9.5"
99
99
  },
100
100
  "peerDependencies": {
101
101
  "eslint": ">=7"