@blumintinc/eslint-plugin-blumint 1.1.8 → 1.1.9

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/lib/index.js CHANGED
@@ -39,7 +39,7 @@ const enforce_firestore_path_utils_1 = require("./rules/enforce-firestore-path-u
39
39
  module.exports = {
40
40
  meta: {
41
41
  name: '@blumintinc/eslint-plugin-blumint',
42
- version: '1.1.8',
42
+ version: '1.1.9',
43
43
  },
44
44
  parseOptions: {
45
45
  ecmaVersion: 2020,
@@ -46,17 +46,21 @@ exports.classMethodsReadTopToBottom = (0, createRule_1.createRule)({
46
46
  const newClassBody = sortedOrder
47
47
  .map((n) => {
48
48
  // Fetch the actual AST node corresponding to the name
49
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
50
49
  const memberNode = node.body.find((member) => getMemberName(member) === n);
50
+ if (!memberNode) {
51
+ return '';
52
+ }
51
53
  const comments = sourceCode.getCommentsBefore(memberNode) || [];
52
- memberNode.range = comments.length > 0
54
+ const nodeRange = memberNode.range;
55
+ const newRange = comments.length > 0
53
56
  ? [
54
- Math.min(memberNode.range[0], Math.min(...comments.map((comment) => comment.range[0]))),
55
- Math.max(memberNode.range[1], Math.max(...comments.map((comment) => comment.range[1]))),
57
+ Math.min(nodeRange[0], Math.min(...comments.map((comment) => comment.range[0]))),
58
+ Math.max(nodeRange[1], Math.max(...comments.map((comment) => comment.range[1]))),
56
59
  ]
57
- : memberNode.range;
58
- return sourceCode.getText(memberNode);
60
+ : nodeRange;
61
+ return sourceCode.getText({ ...memberNode, range: newRange });
59
62
  })
63
+ .filter(Boolean)
60
64
  .join('\n');
61
65
  return context.report({
62
66
  node,
@@ -34,6 +34,10 @@ function isMutableValue(node) {
34
34
  if (node.type === 'CallExpression') {
35
35
  const callee = node.callee;
36
36
  if (callee.type === 'MemberExpression') {
37
+ // Handle both Identifier and non-Identifier property nodes
38
+ if (callee.property.type !== 'Identifier') {
39
+ return false;
40
+ }
37
41
  const methodName = callee.property.name;
38
42
  const mutatingMethods = [
39
43
  'slice',
@@ -20,6 +20,8 @@ exports.default = (0, createRule_1.createRule)({
20
20
  },
21
21
  defaultOptions: [],
22
22
  create(context) {
23
+ // Check if the file is a TypeScript file
24
+ const isTypeScript = context.getFilename().endsWith('.ts') || context.getFilename().endsWith('.tsx');
23
25
  return {
24
26
  VariableDeclaration(node) {
25
27
  // Only check top-level const declarations
@@ -30,18 +32,40 @@ exports.default = (0, createRule_1.createRule)({
30
32
  if (node.parent?.type !== utils_1.AST_NODE_TYPES.Program) {
31
33
  return;
32
34
  }
33
- // Skip if any declaration is a function component or arrow function
35
+ // Skip if any declaration is a function component, arrow function, forwardRef, or memo
34
36
  const shouldSkip = node.declarations.some(declaration => {
35
37
  if (declaration.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
36
38
  return false;
37
39
  }
38
40
  const name = declaration.id.name;
39
41
  const init = declaration.init;
40
- return (
42
+ // Skip if no initializer
43
+ if (!init) {
44
+ return false;
45
+ }
41
46
  // Skip function components (uppercase name + arrow function)
42
- (/^[A-Z]/.test(name) && init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) ||
43
- // Skip any arrow function
44
- init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression);
47
+ if (/^[A-Z]/.test(name) && init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
48
+ return true;
49
+ }
50
+ // Skip any arrow function
51
+ if (init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
52
+ return true;
53
+ }
54
+ // Skip forwardRef and memo calls
55
+ if (init.type === utils_1.AST_NODE_TYPES.CallExpression) {
56
+ if (init.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
57
+ return ['forwardRef', 'memo'].includes(init.callee.name);
58
+ }
59
+ }
60
+ // Skip type assertions on forwardRef and memo calls
61
+ if (init.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
62
+ const expression = init.expression;
63
+ if (expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
64
+ expression.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
65
+ return ['forwardRef', 'memo'].includes(expression.callee.name);
66
+ }
67
+ }
68
+ return false;
45
69
  });
46
70
  if (shouldSkip) {
47
71
  return;
@@ -53,10 +77,11 @@ exports.default = (0, createRule_1.createRule)({
53
77
  }
54
78
  const { name } = declaration.id;
55
79
  const init = declaration.init;
56
- // Skip if no initializer or if it's a dynamic value
80
+ // Skip if no initializer or if it's a dynamic value or class instance
57
81
  if (!init ||
58
82
  init.type === utils_1.AST_NODE_TYPES.CallExpression ||
59
- init.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
83
+ init.type === utils_1.AST_NODE_TYPES.BinaryExpression ||
84
+ init.type === utils_1.AST_NODE_TYPES.NewExpression) {
60
85
  return;
61
86
  }
62
87
  // Check for UPPER_SNAKE_CASE
@@ -73,34 +98,36 @@ exports.default = (0, createRule_1.createRule)({
73
98
  },
74
99
  });
75
100
  }
76
- // Check for as const
77
- const isAsConstExpression = (node) => {
78
- if (node.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
79
- return (node.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
80
- node.typeAnnotation?.typeName?.name === 'const');
81
- }
82
- return false;
83
- };
84
- const shouldHaveAsConst = (node) => {
85
- // Skip if it's already an as const expression
86
- if (isAsConstExpression(node)) {
101
+ // Only check for as const in TypeScript files
102
+ if (isTypeScript) {
103
+ const isAsConstExpression = (node) => {
104
+ if (node.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
105
+ return (node.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
106
+ node.typeAnnotation?.typeName?.name === 'const');
107
+ }
87
108
  return false;
109
+ };
110
+ const shouldHaveAsConst = (node) => {
111
+ // Skip if it's already an as const expression
112
+ if (isAsConstExpression(node)) {
113
+ return false;
114
+ }
115
+ // Check if it's a literal, array, or object that should have as const
116
+ return (node.type === utils_1.AST_NODE_TYPES.Literal ||
117
+ node.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
118
+ node.type === utils_1.AST_NODE_TYPES.ObjectExpression);
119
+ };
120
+ if (shouldHaveAsConst(init)) {
121
+ context.report({
122
+ node: init,
123
+ messageId: 'asConst',
124
+ fix(fixer) {
125
+ const sourceCode = context.getSourceCode();
126
+ const initText = sourceCode.getText(init);
127
+ return fixer.replaceText(init, `${initText} as const`);
128
+ },
129
+ });
88
130
  }
89
- // Check if it's a literal, array, or object that should have as const
90
- return (node.type === utils_1.AST_NODE_TYPES.Literal ||
91
- node.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
92
- node.type === utils_1.AST_NODE_TYPES.ObjectExpression);
93
- };
94
- if (shouldHaveAsConst(init)) {
95
- context.report({
96
- node: init,
97
- messageId: 'asConst',
98
- fix(fixer) {
99
- const sourceCode = context.getSourceCode();
100
- const initText = sourceCode.getText(init);
101
- return fixer.replaceText(init, `${initText} as const`);
102
- },
103
- });
104
131
  }
105
132
  });
106
133
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",