@blumintinc/eslint-plugin-blumint 1.5.2 → 1.5.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.
package/lib/index.js CHANGED
@@ -59,7 +59,7 @@ const no_jsx_in_hooks_1 = require("./rules/no-jsx-in-hooks");
59
59
  module.exports = {
60
60
  meta: {
61
61
  name: '@blumintinc/eslint-plugin-blumint',
62
- version: '1.5.2',
62
+ version: '1.5.4',
63
63
  },
64
64
  parseOptions: {
65
65
  ecmaVersion: 2020,
@@ -137,15 +137,16 @@ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
137
137
  function isTypeExported(typeName) {
138
138
  const sourceCode = context.getSourceCode();
139
139
  const program = sourceCode.ast;
140
- // Check for imported types
141
- const importedTypes = program.body.filter((node) => {
140
+ // Check for imported types first - if found, return true immediately
141
+ // since imported types are already available to consumers
142
+ const hasImportedType = program.body.some((node) => {
142
143
  if (node.type === utils_1.AST_NODE_TYPES.ImportDeclaration) {
143
144
  return node.specifiers.some((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
144
145
  specifier.local.name === typeName);
145
146
  }
146
147
  return false;
147
148
  });
148
- if (importedTypes.length > 0) {
149
+ if (hasImportedType) {
149
150
  return true;
150
151
  }
151
152
  // Check for exported type declarations
@@ -4581,22 +4581,83 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
4581
4581
  }
4582
4582
  return isVerb;
4583
4583
  }
4584
- function isJsxReturnFunction(node) {
4584
+ function isPascalCase(name) {
4585
+ return /^[A-Z][a-zA-Z0-9]*$/.test(name);
4586
+ }
4587
+ function hasPropsParameter(node) {
4588
+ if (!node.params.length)
4589
+ return false;
4590
+ const firstParam = node.params[0];
4591
+ if (firstParam.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
4592
+ return true; // Destructured props
4593
+ }
4594
+ if (firstParam.type === utils_1.AST_NODE_TYPES.Identifier) {
4595
+ const paramName = firstParam.name.toLowerCase();
4596
+ return paramName === 'props';
4597
+ }
4598
+ return false;
4599
+ }
4600
+ function hasJsxReturn(node) {
4601
+ const sourceCode = context.getSourceCode();
4602
+ const text = sourceCode.getText(node);
4603
+ // Check for direct JSX returns
4604
+ if (text.includes('return <') || text.includes('=> <')) {
4605
+ return true;
4606
+ }
4607
+ // Check for JSX assigned to variables then returned
4608
+ if (text.includes('const ') && text.includes('<') && text.includes('return')) {
4609
+ return true;
4610
+ }
4611
+ return false;
4612
+ }
4613
+ function isReactComponent(node) {
4585
4614
  if (node.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration &&
4586
4615
  node.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
4587
4616
  node.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
4588
4617
  return false;
4589
4618
  }
4590
- // Check if function returns JSX
4591
- const sourceCode = context.getSourceCode();
4592
- const text = sourceCode.getText(node);
4593
- return text.includes('return <') || text.includes('=> <');
4619
+ // Get the function name
4620
+ let functionName = '';
4621
+ if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration && node.id) {
4622
+ functionName = node.id.name;
4623
+ }
4624
+ else if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression || node.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
4625
+ const parent = node.parent;
4626
+ if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator && parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
4627
+ functionName = parent.id.name;
4628
+ }
4629
+ }
4630
+ // Check for React component indicators
4631
+ const indicators = [
4632
+ // 1. PascalCase naming
4633
+ functionName && isPascalCase(functionName),
4634
+ // 2. Props parameter
4635
+ hasPropsParameter(node),
4636
+ // 3. JSX return
4637
+ hasJsxReturn(node),
4638
+ // 4. React type annotations
4639
+ (() => {
4640
+ if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
4641
+ const parent = node.parent;
4642
+ if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
4643
+ const id = parent.id;
4644
+ if (id.type === utils_1.AST_NODE_TYPES.Identifier && id.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
4645
+ const typeText = context.getSourceCode().getText(id.typeAnnotation.typeAnnotation);
4646
+ return typeText.includes('React.') || typeText.includes('FC') || typeText.includes('FunctionComponent');
4647
+ }
4648
+ }
4649
+ }
4650
+ return false;
4651
+ })()
4652
+ ];
4653
+ // Consider it a React component if it matches at least 2 indicators
4654
+ return indicators.filter(Boolean).length >= 2;
4594
4655
  }
4595
4656
  return {
4596
4657
  FunctionDeclaration(node) {
4597
4658
  if (!node.id)
4598
4659
  return;
4599
- if (isJsxReturnFunction(node)) {
4660
+ if (isReactComponent(node)) {
4600
4661
  return;
4601
4662
  }
4602
4663
  if (!isVerbPhrase(node.id.name)) {
@@ -4612,7 +4673,7 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
4612
4673
  // Only check if it's a function
4613
4674
  if (node.init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
4614
4675
  node.init?.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
4615
- if (isJsxReturnFunction(node.init)) {
4676
+ if (isReactComponent(node.init)) {
4616
4677
  return;
4617
4678
  }
4618
4679
  if (!isVerbPhrase(node.id.name)) {
@@ -89,6 +89,18 @@ function getObjectUsagesInHook(hookBody, objectName) {
89
89
  }
90
90
  });
91
91
  }
92
+ else if (node.type === utils_1.AST_NODE_TYPES.JSXElement || node.type === utils_1.AST_NODE_TYPES.JSXFragment) {
93
+ // If we find a JSX element, check its attributes for spread operator
94
+ if (node.type === utils_1.AST_NODE_TYPES.JSXElement) {
95
+ node.openingElement.attributes.forEach((attr) => {
96
+ if (attr.type === utils_1.AST_NODE_TYPES.JSXSpreadAttribute &&
97
+ attr.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
98
+ attr.argument.name === objectName) {
99
+ needsEntireObject = true;
100
+ }
101
+ });
102
+ }
103
+ }
92
104
  else if (node.type === utils_1.AST_NODE_TYPES.SpreadElement) {
93
105
  // If we find a spread operator with our target object, consider it as accessing all properties
94
106
  if (node.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
@@ -48,6 +48,9 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
48
48
  create(context, [options]) {
49
49
  const finalOptions = { ...defaultOptions, ...options };
50
50
  function getParameterType(param) {
51
+ if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
52
+ return getParameterType(param.left);
53
+ }
51
54
  if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
52
55
  const typeNode = param.typeAnnotation.typeAnnotation;
53
56
  if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
@@ -55,6 +58,12 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
55
58
  ? typeNode.typeName.name
56
59
  : 'unknown';
57
60
  }
61
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSStringKeyword)
62
+ return 'string';
63
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSNumberKeyword)
64
+ return 'number';
65
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword)
66
+ return 'boolean';
58
67
  return typeNode.type;
59
68
  }
60
69
  return 'unknown';
@@ -95,7 +104,7 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
95
104
  if (shouldIgnoreNode(node))
96
105
  return;
97
106
  const params = node.params;
98
- // Check for too many parameters
107
+ // Check for too many parameters first
99
108
  const minParams = finalOptions.minimumParameters !== undefined
100
109
  ? finalOptions.minimumParameters
101
110
  : defaultOptions.minimumParameters;
@@ -107,7 +116,7 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
107
116
  });
108
117
  return;
109
118
  }
110
- // Check for same type parameters if enabled
119
+ // Then check for same type parameters if enabled
111
120
  if (finalOptions.checkSameTypeParameters && params.length >= 2) {
112
121
  if (hasSameTypeParameters(params)) {
113
122
  context.report({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",