Repository: expo/expo
File: packages/babel-preset-expo/src/widgets-plugin.ts (TypeScript)

Code before:
    visitor: {
      [WidgetizableFunction as 'FunctionDeclaration']: {
        exit(path) {
          if (!t.isBlockStatement(path.node.body)) {
            return;
          }
          // Skip if no 'widget' directive
          if (
            !path.node.body.directives.some(
              (directive) =>
                t.isDirectiveLiteral(directive.value) && directive.value.value === 'widget'
            )
          ) {
            return;
          }
          // Remove the 'widget' directive to avoid runtime overhead
          path.traverse({
            DirectiveLiteral(nodePath) {
              if (nodePath.node.value === 'widget' && nodePath.getFunctionParent() === path) {
                nodePath.parentPath.remove();
              }
            },
          });

          const code = generateWidgetFunctionString(t, path.node);
          replaceWidgetFunction(t, path, code);
        },
      },
    },
  };
}

function generateWidgetFunctionString(
  t: typeof import('@babel/core').types,
  node: t.Function
): string {
  const expression = t.functionExpression(
    null,
    node.params as t.FunctionExpression['params'],
    node.body as t.BlockStatement,
    node.generator,
    node.async
  );
  return generator.generate(expression, { compact: false }).code;
}

function replaceWidgetFunction(
  t: typeof import('@babel/core').types,
  path: NodePath<t.Function>,
  code: string
) {

Review comment (refactor):
nit: in the future it'd be nice to wrap this with a marker, e.g. add require/import to `expo-widgets` (or a subroute like `expo-widgets/internals/utils` or sth) with a helper function that wraps the string to ensure that, without TypeScript, we can guarantee on the "other end" that this has been wrapped properly

Code after:
          if (!isWidgetFunction(path)) {
            return;
          }
          removeWidgetDirective(path.node.body);
          const code = generateWidgetFunctionString(t, path.node);
          const literal = buildTemplateLiteral(t, code);

          if (path.parentPath.isExportDefaultDeclaration()) {
            path.parentPath.replaceWith(t.exportDefaultDeclaration(literal));
            return;
          }

          if (path.node.id) {
            path.replaceWith(
              t.variableDeclaration('var', [t.variableDeclarator(path.node.id, literal)])
            );
          } else {
            path.replaceWith(literal);
          }
        },
      },
      ArrowFunctionExpression: {
        exit(path) {
          if (!isWidgetFunction(path)) {
            return;
          }
          // Check above will guarantee body is a BlockStatement
          removeWidgetDirective(path.node.body as t.BlockStatement);
          const code = generateWidgetFunctionString(t, path.node);
          const literal = buildTemplateLiteral(t, code);
          path.replaceWith(literal);
        },
      },
      ObjectMethod: {
        exit(path) {
          if (!isWidgetFunction(path)) {
            return;
          }
          removeWidgetDirective(path.node.body);
          const code = generateWidgetFunctionString(t, path.node);
          const literal = buildTemplateLiteral(t, code);
          path.replaceWith(t.objectProperty(path.node.key, literal, path.node.computed));
        },
      },
    },
  };

  function isWidgetFunction(path: NodePath<t.Function>) {
    if (!t.isBlockStatement(path.node.body)) {
      return false;
    }