function lintUseStyletron()

in packages/eslint-plugin-baseui/src/deprecated-theme-api.js [436:555]


function lintUseStyletron(context, node) {
  // Is there a useStyletron call in our current scope?
  // Is the theme value captured from the call? Ex: const [css, theme] = useStyletron();
  //   - Is the theme value renamed? Ex: const [css, foo] = useStyletron();
  //   - Is the theme destructured? Ex: const [css, {colors}] = useStyletron();
  // Is the current node invoked in a way that uses the theme in scope?
  //   - Ex: Full destructuring: foreground
  //   - Ex: Concern destructuring: colors.foreground

  // const ancestors = context.getAncestors();
  const scope = context.getScope();
  const themeProperty = deprecatedThemeProperties[node.name];

  if (scope.type === 'function' && scope.block.body.body) {
    // Find all the variable declarations in the function body.
    const declarations = scope.block.body.body.filter(
      (statement) => statement.type === 'VariableDeclaration'
    );

    // Map of variable declaration types and properties:
    // const [css, theme] = useStyletron()
    //       ^..........^                  .id (ArrayPattern)
    //                      ^............^ .init (CallExpression)
    //       ^...........................^ VariableDeclarator
    // ^.................................^ VariableDeclaration

    // Search each declaration for a declarator that invokes
    // useStyletron as the initial value. This declarator
    // will have all the information we need.
    let declarator;
    declarations.forEach((declaration) => {
      declarator = declaration.declarations.find(
        (declarator) =>
          declarator.type === 'VariableDeclarator' &&
          declarator.init &&
          declarator.init.type === 'CallExpression' &&
          declarator.init.callee.name === 'useStyletron'
      );
    });

    if (!declarator) {
      return false;
    }

    if (declarator.id.type === 'ArrayPattern' && declarator.id.elements.length === 2) {
      // Confirm we are accessing the theme index in the returned array.
      // Ex: const [css, theme] = useStyletron();
      // Ex: const [css, {colors}] = useStyletron();
      const themeIndexNode = declarator.id.elements[1];

      if (themeIndexNode.type === 'Identifier') {
        // This implies we are not destructuring the theme object (here at least).
        const localThemeObjectName = themeIndexNode.name;
        if (
          node.parent.type === 'MemberExpression' &&
          node.parent.object.type === 'MemberExpression' &&
          node.parent.object.property.name === themeProperty.concern &&
          node.parent.object.object.type === 'Identifier' &&
          node.parent.object.object.name === localThemeObjectName
        ) {
          // We have verified that the identifier accesses the theme.
          // Ex: theme.colors.foreground
          return true;
        }
      }

      if (themeIndexNode.type === 'ObjectPattern') {
        // Our theme object is being destructured.

        // Check if we are destructuring the theme concern.
        // Ex: const [css, {colors}] = useStyletron();
        // Ex: const [css, {colors: foo}] = useStyletron();
        const concernPropertyNode = themeIndexNode.properties.find(
          (property) => property.key.name === themeProperty.concern
        );

        // TODO(refactor): check if lintStyleFunction can also use this:
        // > node.parent.object.name === concernPropertyNode.value.name

        if (
          concernPropertyNode &&
          // Ensure we are not destructuring further
          concernPropertyNode.value.type === 'Identifier' &&
          node.parent.type === 'MemberExpression' &&
          node.parent.object.type === 'Identifier' &&
          node.parent.object.name === concernPropertyNode.value.name
        ) {
          // We have verified that the identifier accesses the "concern".
          // Ex: colors.foreground
          return true;
        }

        if (concernPropertyNode && concernPropertyNode.value.type === 'ObjectPattern') {
          // We are destructuring even further!

          // Check if we are destructuring the deprecated property in question.
          // Ex: const [css, {colors: {foreground}}] = useStyletron();
          // Ex: const [css, {colors: {foreground: foo}}] = useStyletron();
          const deprecatedProperty = concernPropertyNode.value.properties.find(
            (property) => property.key.name === node.name
          );

          // TODO(refactor): The conditional below is pretty confusing.
          // We should break it up a bit. Also it is exactly the same as
          // the final destructuring logic in lintStyleFunction...
          if (
            deprecatedProperty &&
            (node === deprecatedProperty.key ||
              (node !== deprecatedProperty.value && node.parent.type !== 'MemberExpression'))
          ) {
            return true;
          }
        }
      }
    }
  }

  // If we've reached here then we can't flag anything.
  return false;
}