Repository: microsoft/griffel
Pull request #444 (2023-09-26 21:56:04)

feat: update all rules to lint makeResetStyles and makeStaticStyles
Updates the lint rules to consistently check `makeResetStyles` and `makeStaticStyles`, not just `makeStyles`.

github-actions[bot]: ## 📊 Bundle size report

<sub>🤖 This report was generated against <a href='https://github.com/microsoft/griffel/commit/480aba5a6d8a3b295c10dffe80c2ff4e228f9b3d'>480aba5a6d8a3b295c10dffe80c2ff4e228f9b3d</a></sub>

<!-- Sticky Pull Request Comment -->
layershifter: @NotWoods there is a merge conflicts, can you please check it?

Patch:
diff --git a/change/@griffel-eslint-plugin-aaff19e9-c0d8-41c3-b258-2458bd8cbb7f.json b/change/@griffel-eslint-plugin-aaff19e9-c0d8-41c3-b258-2458bd8cbb7f.json
new file mode 100644
index 0000000..ebc0148
--- /dev/null
+++ b/change/@griffel-eslint-plugin-aaff19e9-c0d8-41c3-b258-2458bd8cbb7f.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "feat: update all rules to lint makeResetStyles and makeStaticStyles",
+  "packageName": "@griffel/eslint-plugin",
+  "email": "tigeroakes@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/packages/eslint-plugin/src/rules/hook-naming.ts b/packages/eslint-plugin/src/rules/hook-naming.ts
index 562d17d..9a62a8f 100644
--- a/packages/eslint-plugin/src/rules/hook-naming.ts
+++ b/packages/eslint-plugin/src/rules/hook-naming.ts
@@ -1,7 +1,7 @@
 import { ESLintUtils } from '@typescript-eslint/utils';
 
 import { createRule } from '../utils/createRule';
-import { isIdentifier, isMakeStylesIdentifier } from '../utils/helpers';
+import { isIdentifier, isMakeStylesCallExpression } from '../utils/helpers';
 
 export const RULE_NAME = 'hook-naming';
 
@@ -27,7 +27,11 @@ export const hookNamingRule: ReturnType<ReturnType<typeof ESLintUtils.RuleCreato
   create(context) {
     return {
       VariableDeclarator(node) {
-        if (node.init !== null && node.init.type === 'CallExpression' && isMakeStylesIdentifier(node.init.callee)) {
+        if (
+          node.init !== null &&
+          node.init.type === 'CallExpression' &&
+          isMakeStylesCallExpression(node.init, 'makeStyles', 'makeStaticStyles', 'makeResetStyles')
+        ) {
           const { id } = node;
           if (isIdentifier(id) && !id.name.startsWith('use')) {
             context.report({
diff --git a/packages/eslint-plugin/src/rules/no-shorthands.ts b/packages/eslint-plugin/src/rules/no-shorthands.ts
index ed53028..04fd124 100644
--- a/packages/eslint-plugin/src/rules/no-shorthands.ts
+++ b/packages/eslint-plugin/src/rules/no-shorthands.ts
@@ -2,7 +2,7 @@ import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
 import * as CSS from 'csstype';
 
 import { createRule } from '../utils/createRule';
-import { isIdentifier, isLiteral, isMakeStylesIdentifier, isObjectExpression, isProperty } from '../utils/helpers';
+import { isIdentifier, isLiteral, isMakeStylesCallExpression, isObjectExpression, isProperty } from '../utils/helpers';
 import { buildShorthandSplitter } from '../utils/buildShorthandSplitter';
 
 export const RULE_NAME = 'no-shorthands';
@@ -151,7 +151,7 @@ export const noShorthandsRule: ReturnType<ReturnType<typeof ESLintUtils.RuleCrea
   create(context) {
     return {
       CallExpression(node) {
-        if (isMakeStylesIdentifier(node.callee)) {
+        if (isMakeStylesCallExpression(node, 'makeStyles')) {
           const argument = node.arguments[0];
 
           if (isObjectExpression(argument)) {
diff --git a/packages/eslint-plugin/src/rules/pseudo-element-naming.ts b/packages/eslint-plugin/src/rules/pseudo-element-naming.ts
index a1db5e8..742f295 100644
--- a/packages/eslint-plugin/src/rules/pseudo-element-naming.ts
+++ b/packages/eslint-plugin/src/rules/pseudo-element-naming.ts
@@ -1,7 +1,7 @@
 import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
 
 import { createRule } from '../utils/createRule';
-import { isStringLiteral, isMakeStylesIdentifier, isObjectExpression, isProperty } from '../utils/helpers';
+import { isMakeStylesCallExpression, isObjectExpression, isProperty, isStringLiteral } from '../utils/helpers';
 
 export const RULE_NAME = 'pseudo-element-naming';
 
@@ -52,11 +52,11 @@ export const pseudoElementNamingRule: ReturnType<ReturnType<typeof ESLintUtils.R
   create(context) {
     return {
       CallExpression(node) {
-        if (isMakeStylesIdentifier(node.callee)) {
-          const argument = node.arguments[0];
-
-          if (isObjectExpression(argument)) {
-            const invalidPseudoElementProperties = findInvalidPseudoElementProperties(argument, true);
+        const argument = node.arguments[0];
+        if (isObjectExpression(argument)) {
+          const isMakeStyles = isMakeStylesCallExpression(node, 'makeStyles');
+          if (isMakeStyles || isMakeStylesCallExpression(node, 'makeResetStyles')) {
+            const invalidPseudoElementProperties = findInvalidPseudoElementProperties(argument, isMakeStyles);
 
             invalidPseudoElementProperties.forEach(invalidPseudoElementProperty => {
               context.report({
diff --git a/packages/eslint-plugin/src/rules/styles-file.ts b/packages/eslint-plugin/src/rules/styles-file.ts
index 38f2957..0cb876a 100644
--- a/packages/eslint-plugin/src/rules/styles-file.ts
+++ b/packages/eslint-plugin/src/rules/styles-file.ts
@@ -1,5 +1,6 @@
 import { createRule } from '../utils/createRule';
 import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
+import { isMakeStylesCallExpression } from '../utils/helpers';
 
 const MATCHING_PACKAGES = new Set(['@fluentui/react-components', '@griffel/core', '@griffel/react']);
 const STYLES_FILE_PATTERN = /^.*\.(styles)\.[j|t]s$/;
@@ -28,6 +29,7 @@ function isMakeStylesImport(node: TSESTree.ImportDeclaration) {
       if ('imported' in specifier) {
         return (
           specifier.imported.name === 'makeStyles' || // import { makeStyles } from
+          specifier.imported.name === 'makeStaticStyles' || // import { makeStaticStyles } from
           specifier.imported.name === 'makeResetStyles' // import { makeResetStyles } from
         );
       }
@@ -37,17 +39,6 @@ function isMakeStylesImport(node: TSESTree.ImportDeclaration) {
   );
 }
 
-/**
- * @param callExpression CallExpression node in AST
- * @returns if this is a makeStyles call or not
- */
-function isMakeStyleCallExpression({ callee }: TSESTree.CallExpression) {
-  return (
-    ('name' in callee && callee.name === 'makeStyles') || // makeStyles({})
-    ('property' in callee && 'name' in callee.property && callee.property.name === 'makeStyles') // something.makeStyles({})
-  );
-}
-
 export const RULE_NAME = 'styles-file';
 
 export const stylesFileRule: ReturnType<ReturnType<typeof ESLintUtils.RuleCreator>> = createRule({
@@ -69,7 +60,7 @@ export const stylesFileRule: ReturnType<ReturnType<typeof ESLintUtils.RuleCreato
     const fileName = context.getFilename();
 
     let isMakeStylesImported = false;
-    const makeStylesDeclarations = []; // contains constants from makeStyles calls: `const useStyles = makeStyles({})`
+    const makeStylesDeclarations: string[] = []; // contains constants from makeStyles calls: `const useStyles = makeStyles({})`
 
     return {
       ImportDeclaration(node) {
@@ -78,7 +69,10 @@ export const stylesFileRule: ReturnType<ReturnType<typeof ESLintUtils.RuleCreato
         }
       },
       CallExpression(node) {
-        if (isMakeStylesImported && isMakeStyleCallExpression(node)) {
+        if (
+          isMakeStylesImported &&
+          isMakeStylesCallExpression(node, 'makeStyles', 'makeStaticStyles', 'makeResetStyles')
+        ) {
           if (!isStylesFile(fileName)) {
             context.report({
               messageId: 'foundMakeStylesUsage',
@@ -93,7 +87,7 @@ export const stylesFileRule: ReturnType<ReturnType<typeof ESLintUtils.RuleCreato
             if (
               isMakeStylesImported &&
               declaration.init?.type === 'CallExpression' &&
-              isMakeStyleCallExpression(declaration.init)
+              isMakeStylesCallExpression(declaration.init, 'makeStyles', 'makeStaticStyles', 'makeResetStyles')
             ) {
               if (declaration.id.type === 'Identifier') {
                 makeStylesDeclarations.push(declaration.id.name);
diff --git a/packages/eslint-plugin/src/utils/helpers.ts b/packages/eslint-plugin/src/utils/helpers.ts
index 272deda..60e5be6 100644
--- a/packages/eslint-plugin/src/utils/helpers.ts
+++ b/packages/eslint-plugin/src/utils/helpers.ts
@@ -6,19 +6,44 @@ type IsHelper<NodeType extends AST_NODE_TYPES> = (node: TSESTree.Node | null | u
 
 export const isIdentifier: IsHelper<AST_NODE_TYPES.Identifier> = ASTUtils.isIdentifier;
 export const isLiteral: IsHelper<AST_NODE_TYPES.Literal> = ASTUtils.isNodeOfType(AST_NODE_TYPES.Literal);
+export const isMemberExpression: IsHelper<AST_NODE_TYPES.MemberExpression> = ASTUtils.isNodeOfType(
+  AST_NODE_TYPES.MemberExpression,
+);
 export const isObjectExpression: IsHelper<AST_NODE_TYPES.ObjectExpression> = ASTUtils.isNodeOfType(
   AST_NODE_TYPES.ObjectExpression,
 );
 export const isProperty: IsHelper<AST_NODE_TYPES.Property> = ASTUtils.isNodeOfType(AST_NODE_TYPES.Property);
 
-export function isMakeStylesIdentifier(node: TSESTree.Node | null | undefined): node is TSESTree.Identifier {
-  return isIdentifier(node) && node.name === 'makeStyles';
+export function isStringLiteral(node: TSESTree.Node | null | undefined): node is TSESTree.StringLiteral {
+  return isLiteral(node) && typeof node.value === 'string';
 }
 
-export function isMakeResetStylesIdentifier(node: TSESTree.Node | null | undefined): node is TSESTree.Identifier {
-  return isIdentifier(node) && node.name === 'makeResetStyles';
-}
+type MakeStylesName = 'makeStyles' | 'makeStaticStyles' | 'makeResetStyles';
 
-export function isStringLiteral(node: TSESTree.Node | null | undefined): node is TSESTree.StringLiteral {
-  return isLiteral(node) && typeof node.value === 'string';
+/**
+ * Checks if the given node is a `makeStyles` call expression.
+ * @param node Call expression node in AST to check.
+ * @param functionNames Function names to check, such as `makeStyles`, `makeStaticStyles`, `makeResetStyles`.
+ * Must pass at least one name.
+ */
+export function isMakeStylesCallExpression(
+  node: TSESTree.CallExpression,
+  ...functionNames: [MakeStylesName, ...MakeStylesName[]]
+): boolean {
+  const { callee } = node;
+  const names: ReadonlySet<string> = new Set(functionNames);
+
+  if (isIdentifier(callee)) {
+    // makeStyles({})
+    return names.has(callee.name);
+  } else if (isMemberExpression(callee)) {
+    return (
+      // something.makeStyles({})
+      (isIdentifier(callee.property) && names.has(callee.property.name)) ||
+      // something['makeStyles']({})
+      (isStringLiteral(callee.property) && names.has(callee.property.value))
+    );
+  } else {
+    return false;
+  }
 }


Test patch:
diff --git a/packages/eslint-plugin/src/rules/hook-naming.test.ts b/packages/eslint-plugin/src/rules/hook-naming.test.ts
index a144bcd..41f1255 100644
--- a/packages/eslint-plugin/src/rules/hook-naming.test.ts
+++ b/packages/eslint-plugin/src/rules/hook-naming.test.ts
@@ -14,13 +14,33 @@ const ruleTester = new TSESLint.RuleTester({
 ruleTester.run(RULE_NAME, hookNamingRule, {
   valid: [
     {
-      name: 'named useStyles',
+      name: 'named useStyles from makeStyles',
       code: `
 import { makeStyles } from '@griffel/react';
 
 export const useStyles = makeStyles({
   root: { backgroundColor: 'red' },
 });
+`,
+    },
+    {
+      name: 'named useStyles from makeStaticStyles',
+      code: `
+import { makeStaticStyles } from '@griffel/react';
+
+export const useStyles = makeStaticStyles({
+  body: { backgroundColor: 'red' },
+});
+`,
+    },
+    {
+      name: 'named useStyles from makeResetStyles',
+      code: `
+import { makeResetStyles } from '@griffel/react';
+
+export const useClass = makeResetStyles({
+  backgroundColor: 'red'
+});
 `,
     },
     {
@@ -58,6 +78,28 @@ export const styles = makeStyles({
     ':hover': { backgroundColor: 'red' }
   },
 });
+`,
+      errors: [{ messageId: 'invalidMakeStylesHookNameFound' }],
+    },
+    {
+      name: 'named with get from makeStaticStyles',
+      code: `
+import { makeStaticStyles } from '@griffel/react';
+
+export const getStyles = makeStaticStyles({
+  body: { backgroundColor: 'red' },
+});
+`,
+      errors: [{ messageId: 'invalidMakeStylesHookNameFound' }],
+    },
+    {
+      name: 'name is only class from makeResetStyles',
+      code: `
+import { makeResetStyles } from '@griffel/react';
+
+export const className = makeResetStyles({
+  backgroundColor: 'red'
+});
 `,
       errors: [{ messageId: 'invalidMakeStylesHookNameFound' }],
     },
diff --git a/packages/eslint-plugin/src/rules/no-shorthands.test.ts b/packages/eslint-plugin/src/rules/no-shorthands.test.ts
index 1467d76..36197ff 100644
--- a/packages/eslint-plugin/src/rules/no-shorthands.test.ts
+++ b/packages/eslint-plugin/src/rules/no-shorthands.test.ts
@@ -31,6 +31,20 @@ import { makeStyles } from '@griffel/react';
 export const useStyles = makeStyles({
   background: { backgroundColor: 'red' },
 });
+`,
+    },
+    {
+      name: 'CSS shorthands can be used in makeResetStyles and makeStaticStyles',
+      code: `
+import { makeResetStyles, makeStaticStyles } from '@griffel/react';
+
+export const useClass = makeResetStyles({
+  background: 'red'
+});
+
+export const useStyles = makeStaticStyles({
+  body: { background: 'red' }
+})
 `,
     },
   ],
diff --git a/packages/eslint-plugin/src/rules/pseudo-element-naming.test.ts b/packages/eslint-plugin/src/rules/pseudo-element-naming.test.ts
index e6c01d5..0c3f871 100644
--- a/packages/eslint-plugin/src/rules/pseudo-element-naming.test.ts
+++ b/packages/eslint-plugin/src/rules/pseudo-element-naming.test.ts
@@ -26,11 +26,15 @@ export const useStyles = makeStyles({
     {
       name: 'with Valid Pseudo Element',
       code: `
-    import { makeStyles } from '@griffel/react';
+    import { makeResetStyles, makeStyles } from '@griffel/react';
 
     export const useStyles = makeStyles({
       root: { '::before': { backgroundColor: 'red' } },
     });
+
+    export const useStyles = makeResetStyles({
+      '::before': { backgroundColor: 'red' }
+    });
     `,
     },
     {
@@ -62,6 +66,22 @@ export const useStyles = makeStyles({
     `,
       errors: [{ messageId: 'invalidPseudoElementNameFound' }],
     },
+    {
+      name: 'Invalid Pseudo Element in makeResetStyles',
+      code: `
+    import { makeResetStyles } from '@griffel/react';
+    export const useStyles = makeResetStyles({
+      ':before': { backgroundColor: 'red' },
+    });
+    `,
+      output: `
+    import { makeResetStyles } from '@griffel/react';
+    export const useStyles = makeResetStyles({
+      '::before': { backgroundColor: 'red' },
+    });
+    `,
+      errors: [{ messageId: 'invalidPseudoElementNameFound' }],
+    },
     {
       name: 'Invalid Pseudo Element in a selector',
       code: `