@babel/traverse#NodePath TypeScript Examples

The following examples show how to use @babel/traverse#NodePath. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From vidact with MIT License 7 votes vote down vote up
export function getNodePathForType<
  Type extends t.Node["type"],
  NodeType = Extract<t.Node, { type: Type }>,
  NodePathType = NodePath<NodeType>
>(node: t.Node, type: Type) {
  const paths: NodePathType[] = [];

  traverse(node, {
    [type]: (path: NodePathType) => {
      paths.push(path);
    }
  });

  return paths;
}
Example #2
Source File: babelInlineConverters.ts    From react-native-decompiler with GNU Affero General Public License v3.0 6 votes vote down vote up
private slicedToArrayFunction(path: NodePath<t.VariableDeclarator>) {
    if (path.removed || !t.isCallExpression(path.node.init) || path.node.init.arguments.length !== 0) return;

    let hasErrorString = false;
    path.traverse({
      StringLiteral: (stringPath) => {
        if (stringPath.node.value === 'Invalid attempt to destructure non-iterable instance') {
          hasErrorString = true;
        }
      },
    });
    if (!hasErrorString) return;

    this.debugLog('replace var incline babel slicedToArray function:');
    this.debugLog(this.debugPathToCode(path));

    path.get('init').replaceWith(t.callExpression(t.identifier('require'), [t.stringLiteral('@babel/runtime/helpers/slicedToArray')]));
  }
Example #3
Source File: resolveJsxComponent.ts    From react-optimized-image with MIT License 6 votes vote down vote up
resolveObjectProperty = (path: NodePath<ObjectExpression>, property: ObjectProperty): string[] => {
  let bindings: string[] = [];
  const parent = path.findParent(() => true);

  if (parent.node.type === 'ObjectProperty') {
    bindings = [...resolveObjectProperty(parent.findParent(() => true) as NodePath<ObjectExpression>, parent.node)];
  } else if (parent.node.type === 'VariableDeclarator' && parent.node.id.type === 'Identifier') {
    bindings.push(parent.node.id.name);
  }

  bindings.push(property.key.name);

  return bindings;
}
Example #4
Source File: webpackParser.ts    From react-native-decompiler with GNU Affero General Public License v3.0 6 votes vote down vote up
private parseArray(file: File, ast: NodePath<ArrayExpression>, modules: Module[]): void {
    ast.get('elements').forEach((element, i) => {
      if (!element.isFunctionExpression()) return;
      if (element.node.body.body.length === 0) return;

      const dependencyValues: number[] = [];
      const requireIdentifer = element.node.params[2];
      if (isIdentifier(requireIdentifer)) {
        element.traverse({
          CallExpression: (dependencyPath) => {
            if (!isIdentifier(dependencyPath.node.callee) || !isNumericLiteral(dependencyPath.node.arguments[0])) return;
            if (dependencyPath.scope.bindingIdentifierEquals(dependencyPath.node.callee.name, requireIdentifer)) {
              dependencyValues[dependencyPath.node.arguments[0].value] = dependencyPath.node.arguments[0].value;
            }
          },
        });
      }

      const newModule = new Module(file, element, i, dependencyValues, this.PARAM_MAPPING);
      newModule.calculateFields();
      modules[i] = newModule;
    });
  }
Example #5
Source File: resolveJsxComponent.ts    From react-optimized-image with MIT License 6 votes vote down vote up
resolveJsxComponent = (types: Babel['types'], path: NodePath<JSXElement>): string | undefined => {
  // check if it is a possible react-optimized-image component before proceeding further
  const srcAttribute = getAttribute(path, 'src');

  if (!srcAttribute) {
    return;
  }

  const requireName = getRelevantRequireString(types, srcAttribute);

  // check if the imported src is not an image in case an extension is present
  if ((!requireName || !requireName.match(/\.(jpe?g|png|svg|gif|webp)($|\?)/gi)) && requireName !== '') {
    return;
  }

  // it is now likely to be a react-optimized-image component, so start resolving

  // check for a normal opening element (<Img ...)
  if (path.node.openingElement.name.type === 'JSXIdentifier') {
    const binding = path.scope.getBinding(path.node.openingElement.name.name);
    const component = getImportedJsxComponent(binding);

    return component;
  }

  // check for an object opening element (<styles.Img ...)
  if (path.node.openingElement.name.type === 'JSXMemberExpression') {
    const objectBindings = resolveJsxMemberExpression(path.node.openingElement.name);
    const resolvedBinding = resolveObject(types, path, objectBindings);
    const component = getImportedJsxComponent(resolvedBinding);

    return component;
  }
}
Example #6
Source File: component.ts    From reskript with MIT License 6 votes vote down vote up
resolveCalleeName = (path: NodePath<babel.types.CallExpression>) => {
    const callee = path.get('callee');

    if (callee.isIdentifier()) {
        return callee.node.name;
    }
    if (callee.isMemberExpression()) {
        const property = callee.get('property');
        return property.isIdentifier() ? property.node.name : '';
    }
    return '';
}
Example #7
Source File: get-import-nodes.ts    From prettier-plugin-sort-imports with Apache License 2.0 6 votes vote down vote up
getImportNodes = (code: string, options?: ParserOptions) => {
    const importNodes: ImportDeclaration[] = [];
    const ast = babelParser(code, {
        ...options,
        sourceType: 'module',
    });

    traverse(ast, {
        ImportDeclaration(path: NodePath<ImportDeclaration>) {
            const tsModuleParent = path.findParent((p) =>
                isTSModuleDeclaration(p),
            );
            if (!tsModuleParent) {
                importNodes.push(path.node);
            }
        },
    });

    return importNodes;
}
Example #8
Source File: component.ts    From reskript with MIT License 6 votes vote down vote up
resolveComponentName = (declaration: NodePath<FunctionDeclaration>, filename: string | undefined) => {
    const functionName = declaration.node.id?.name;

    if (functionName) {
        return functionName;
    }

    if (!filename) {
        return 'Unknown';
    }

    const file = path.basename(filename, path.extname(filename));
    return file === 'index' ? path.dirname(filename).split(path.sep).pop() : file;
}
Example #9
Source File: plugin.ts    From react-native-decompiler with GNU Affero General Public License v3.0 6 votes vote down vote up
protected getModuleDependency(path: NodePath<t.CallExpression>): Module | null {
    if (!t.isIdentifier(path.node.callee)) return null;
    if (!t.isNumericLiteral(path.node.arguments[0]) && !t.isMemberExpression(path.node.arguments[0]) && !t.isStringLiteral(path.node.arguments[0])) return null;
    if (path.scope.getBindingIdentifier(path.node.callee.name)?.start !== this.module.requireParam?.start) return null;

    if (t.isMemberExpression(path.node.arguments[0]) && t.isNumericLiteral(path.node.arguments[0].property)) {
      return this.moduleList[this.module.dependencies[path.node.arguments[0].property.value]] ?? null;
    }

    if (t.isStringLiteral(path.node.arguments[0])) {
      const nonNpmRegexTest = /\.\/([0-9]+)/.exec(path.node.arguments[0].value);
      if (nonNpmRegexTest != null) {
        return this.moduleList[this.module.dependencies[+nonNpmRegexTest[1]]];
      }
      return this.moduleList.find((mod) => t.isStringLiteral(path.node.arguments[0]) && mod?.moduleName === path.node.arguments[0].value) ?? null;
    }

    if (t.isNumericLiteral(path.node.arguments[0])) {
      return this.moduleList[this.module.dependencies[path.node.arguments[0].value]] ?? null;
    }

    return null;
  }
Example #10
Source File: utils.ts    From vidact with MIT License 6 votes vote down vote up
export function createNodePath<T extends t.Node>(node: T) {
  const path: NodePath<T> = new NodePath(new Hub(null, {}), null);
  path.node = node;
  return path;
}
Example #11
Source File: plugin.ts    From react-native-decompiler with GNU Affero General Public License v3.0 6 votes vote down vote up
protected variableIsForDependency(path: NodePath<t.VariableDeclarator> | NodePath<t.ImportDeclaration>, dep: string | string[]): boolean {
    const depArray = dep instanceof Array ? dep : [dep];

    if (path.isVariableDeclarator()) {
      const callExpression = path.get('init');
      if (!callExpression.isCallExpression()) return false;

      const requireValue = t.isStringLiteral(callExpression.node.arguments[0]) ? callExpression.node.arguments[0].value : null;
      const dependencyName = this.getModuleDependency(callExpression)?.moduleName ?? requireValue ?? '';

      return depArray.includes(dependencyName);
    }
    if (path.isImportDeclaration()) {
      if (!t.isStringLiteral(path.node.source)) return false;

      return depArray.includes(path.node.source.value);
    }
    return false;
  }
Example #12
Source File: createStatementUpdater.ts    From vidact with MIT License 6 votes vote down vote up
export function createStatementUpdater(
  statement: NodePath | t.Statement,
  scope: Scope | t.Identifier,
  name = STATEMENT_EXECUTER_VAR
): [t.VariableDeclaration, t.ExpressionStatement, t.Identifier] {
  const uid = t.isIdentifier(scope) ? scope : scope.generateUidIdentifier(name);
  const node = "node" in statement ? statement.node : statement;
  const block = t.isBlockStatement(node)
    ? node
    : t.blockStatement([node as t.Statement]);

  const nodeToReplace = t.variableDeclaration("const", [
    t.variableDeclarator(uid, t.arrowFunctionExpression([], block))
  ]);
  const callExpression = t.expressionStatement(t.callExpression(uid, []));

  return [nodeToReplace, callExpression, uid];
}
Example #13
Source File: plugin.ts    From react-native-decompiler with GNU Affero General Public License v3.0 6 votes vote down vote up
/**
   * [DEBUG] Returns the code of the path
   * @param path The path to generate code from
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  protected debugPathToCode(path: NodePath<any>): string {
    if (!debug(this.getDebugName()).enabled) return '';
    return generator({
      ...this.module.originalFile.program,
      type: 'Program',
      body: [t.isStatement(path.node) ? path.node : t.expressionStatement(path.node)],
    }).code;
  }
Example #14
Source File: preprocessor.ts    From prettier-plugin-sort-imports with Apache License 2.0 5 votes vote down vote up
export function preprocessor(code: string, options: PrettierOptions) {
    const {
        importOrderParserPlugins,
        importOrder,
        importOrderCaseInsensitive,
        importOrderSeparation,
        importOrderGroupNamespaceSpecifiers,
        importOrderSortSpecifiers,
    } = options;

    const importNodes: ImportDeclaration[] = [];
    const parserOptions: ParserOptions = {
        sourceType: 'module',
        plugins: getExperimentalParserPlugins(importOrderParserPlugins),
    };

    const ast = babelParser(code, parserOptions);
    const interpreter = ast.program.interpreter;

    traverse(ast, {
        ImportDeclaration(path: NodePath<ImportDeclaration>) {
            const tsModuleParent = path.findParent((p) =>
                isTSModuleDeclaration(p),
            );
            if (!tsModuleParent) {
                importNodes.push(path.node);
            }
        },
    });

    // short-circuit if there are no import declaration
    if (importNodes.length === 0) return code;

    const allImports = getSortedNodes(importNodes, {
        importOrder,
        importOrderCaseInsensitive,
        importOrderSeparation,
        importOrderGroupNamespaceSpecifiers,
        importOrderSortSpecifiers,
    });

    return getCodeFromAst(allImports, code, interpreter);
}
Example #15
Source File: moduleFinder.ts    From react-native-decompiler with GNU Affero General Public License v3.0 5 votes vote down vote up
abstract evaluate(path: NodePath<FunctionExpression>): void;
Example #16
Source File: resolveJsxComponent.ts    From react-optimized-image with MIT License 5 votes vote down vote up
resolveObject = (types: Babel['types'], path: NodePath<JSXElement>, bindings: string[]): Binding | undefined => {
  if (bindings.length < 2) {
    return;
  }

  const variableName = bindings[bindings.length - 1];
  const object = path.scope.getBinding(bindings[0]);
  if (!object) {
    return;
  }

  const program = path.findParent((node) => node.isProgram());
  let declarationPath: any = null; // eslint-disable-line
  let initializer;

  // search for object declaration
  program.traverse({
    // styles.StyledImg = ...
    MemberExpression(exPath: NodePath<MemberExpression>) {
      if (exPath.node.property && exPath.node.property.name === variableName) {
        const exBindings = resolveMemberExpression(exPath.node);

        if (arraysMatch(bindings, exBindings) && exPath.parent.type === 'AssignmentExpression') {
          declarationPath = exPath;
          initializer = exPath.parent.right;
          exPath.stop();
        }
      }
    },

    // const styles = { StyledImg: ... }
    ObjectProperty(opPath: NodePath<ObjectProperty>) {
      if (opPath.node.key && opPath.node.key.type === 'Identifier' && opPath.node.key.name === variableName) {
        const exBindings = resolveObjectProperty(
          opPath.findParent(() => true) as NodePath<ObjectExpression>,
          opPath.node,
        );

        if (arraysMatch(bindings, exBindings)) {
          declarationPath = opPath;
          initializer = opPath.node.value;
          opPath.stop();
        }
      }
    },
  });

  if (!declarationPath) {
    return;
  }

  declarationPath = declarationPath as NodePath<MemberExpression>;

  // mock a binding
  const binding: Partial<Binding> = {
    kind: 'const',
    scope: declarationPath.scope,
    identifier: types.identifier(variableName),
    path: {
      ...(declarationPath as any), // eslint-disable-line
      node: types.variableDeclarator(
        types.objectPattern([types.objectProperty(types.identifier(variableName), types.identifier(variableName))]),
        initializer,
      ),
    },
  };

  return binding as Binding;
}
Example #17
Source File: index.ts    From reskript with MIT License 5 votes vote down vote up
insertImportHook = (program: NodePath<babel.types.Program>) => {
    const expression = types.importDeclaration(
        [types.importDefaultSpecifier(types.identifier('useComponentFile'))],
        types.stringLiteral(HOOK_MODULE)
    );
    const [inserted] = program.unshiftContainer('body', expression);
    return inserted;
}
Example #18
Source File: getImpactfulIdentifiers.ts    From vidact with MIT License 5 votes vote down vote up
export function getImpactfulIdentifiers(
  node: t.Node,
  scope: Scope,
  parentPath: NodePath
) {
  const impactfulIdentifiers: [DependencyType, string][] = [];
  if (t.isIdentifier(node) && !isElementVar(node.name)) {
    impactfulIdentifiers.push(["local", node.name]);
  }

  traverse(
    node,
    {
      Identifier(p) {
        if (t.isMemberExpression(p.container)) {
          const parentNode = p.container as t.MemberExpression;
          if (t.isIdentifier(parentNode.object)) {
            if (p.node.name !== PROP_VAR) {
              if (parentNode.object.name === PROP_VAR) {
                impactfulIdentifiers.push(["prop", parentNode.property.name]);
              } else if (
                parentNode.object.name === p.node.name &&
                p.node.name !== "Object" &&
                !isElementVar(p.node.name)
              ) {
                impactfulIdentifiers.push(["local", p.node.name]);
              }
            }
          }
        } else {
          if (t.isCallExpression(p.container)) {
            const parentNode = p.container as t.CallExpression;
            if (
              t.isIdentifier(parentNode.callee) &&
              parentNode.callee.name === "addPropTransaction"
            ) {
              return;
            }
          }

          if (
            t.isObjectProperty(p.container) ||
            (parentPath.scope !== p.scope &&
              !parentPath.scope.hasBinding(p.node.name))
          ) {
            p.skip();
          } else if (!isElementVar(p.node.name)) {
            impactfulIdentifiers.push(["local", p.node.name]);
          }
        }
      }
    },
    scope,
    {},
    parentPath
  );

  return impactfulIdentifiers;
}
Example #19
Source File: babelClassEvaluator.ts    From react-native-decompiler with GNU Affero General Public License v3.0 5 votes vote down vote up
private classCreatePath?: NodePath<t.VariableDeclarator>;
Example #20
Source File: tsx-wrapper.ts    From a18n with MIT License 4 votes vote down vote up
wrapCode = (
  code: string,
  {
    filePath,
    basePath,
    moduleName,
    namespace,
    checkOnly = false,
    moduleNameUpdate = true,
  }: WrapOptions & {
    filePath?: string
  },
): {
  output: string
  sourceTexts: SourceText[]
} => {
  if (code.includes(LIB_IGNORE_FILE)) {
    return {
      output: code,
      sourceTexts: [],
    }
  }

  const ast = parse(code)
  const existModuleName = extractModuleName(ast)
  const newModuleName: string | undefined = (() => {
    if (!moduleName) return existModuleName
    if (existModuleName && !moduleNameUpdate) return existModuleName

    assert(filePath, 'filePath is required when moduleName is specified')
    assert(basePath, 'basePath is required when moduleName is specified')
    const { dir, base, name, ext } = path.parse(filePath)
    switch (moduleName) {
      case 'fileName': {
        return name
      }
      case 'fileDirAndName': {
        const relativeDir = relative(basePath, dir)
        const lastDir = relativeDir.split(/\/|\\/).pop()
        if (lastDir) {
          return `${lastDir}/${name}`
        } else {
          return name
        }
      }
      case 'filePath': {
        const relativeDir = relative(basePath, dir)
        return (relativeDir + '/' + name)
          .split(/\/|\\/)
          .filter(Boolean)
          .join('/')
      }
      default:
        return assertNever(moduleName)
    }
  })()

  let libUsed = false
  const markLibUsed = (): void => {
    libUsed = true
  }
  let libImportStatus = {
    imported: false,
    required: false,
    namespace: undefined as string | undefined,
  }

  let importStatementCount = 0
  let requireCallExpressionCount = 0

  const lines = code.split('\n')

  let sourceTexts = [] as SourceText[]
  const addStaticText = (node: t.Node, text: string): void => {
    sourceTexts.push(
      toStaticText(node, text, filePath ?? '', lines, newModuleName),
    )
  }
  const addDynamicText = (node: t.Node, parts: string[]) => {
    sourceTexts.push(
      toDynamicText(node, parts, filePath ?? '', lines, newModuleName),
    )
  }

  traverse(ast, {
    enter(path) {
      const node = path.node
      const lineStart = node.loc ? node.loc.start.line - 1 : -1
      const line = lines[lineStart]
      const lineAbove = lines[lineStart - 1]
      if (
        lineAbove?.includes(LIB_IGNORE_LINE) ||
        line?.includes(LIB_IGNORE_LINE)
      ) {
        return
      }

      try {
        switch (node.type) {
          // '中文' => a18n('中文')
          case 'StringLiteral': {
            if (needTranslate(node.value)) {
              // ignore when it's already: a18n(’中文’)
              const parent = path.parent
              if (
                parent.type === 'CallExpression' &&
                t.isIdentifier(parent.callee) &&
                parent.callee.name === LIB_IDENTIFIER
              ) {
                markLibUsed()
                break
              }

              // <input placeholder="中文" /> => <input placeholder={a18n('中文') />
              if (t.isJSXAttribute(parent)) {
                if (checkOnly) {
                  addStaticText(node, node.value)
                } else {
                  path.replaceWith(
                    t.jsxExpressionContainer(
                      t.callExpression(t.identifier(LIB_IDENTIFIER), [
                        t.stringLiteral(node.value),
                      ]),
                    ),
                  )
                }
                markLibUsed()
              } else if (
                !(t.isProperty(parent) && parent.key === node) &&
                !t.isTSLiteralType(parent) &&
                !t.isTSEnumMember(parent)
              ) {
                if (checkOnly) {
                  addStaticText(node, node.value)
                } else {
                  path.replaceWith(
                    t.callExpression(t.identifier(LIB_IDENTIFIER), [
                      t.stringLiteral(node.value),
                    ]),
                  )
                }
                markLibUsed()
              }
            }
            break
          }

          // `中文${someVar}` => a18n`中文${someVar}`
          case 'TemplateLiteral': {
            const { quasis } = node
            if (
              quasis.some((q) => needTranslate(q.value.cooked ?? q.value.raw))
            ) {
              const parent = path.parent
              if (parent.type === 'TaggedTemplateExpression') {
                // ignore when it's already wrapped: a18n`中文${someVar}`
                if (
                  t.isIdentifier(parent.tag) &&
                  parent.tag.name === LIB_IDENTIFIER
                ) {
                  markLibUsed()
                  break
                }
                // ignore when it's already wrapped: a18n.anyMethod`中文${someVar}`
                if (
                  t.isMemberExpression(parent.tag) &&
                  t.isIdentifier(parent.tag.object) &&
                  parent.tag.object.name === LIB_IDENTIFIER
                ) {
                  markLibUsed()
                  break
                }
              } else {
                if (checkOnly) {
                  addDynamicText(
                    node,
                    quasis.map((q) => q.value.cooked ?? q.value.raw),
                  )
                } else {
                  path.replaceWith(
                    t.taggedTemplateExpression(
                      t.identifier(LIB_IDENTIFIER),
                      node,
                    ),
                  )
                }
                markLibUsed()
              }
            }
            break
          }

          // single jsxtext:
          // <div>你好<strong>{userName}</strong></div>
          // =>
          // <div>{a18n("你好")}<strong>{userName}</strong></div>
          //
          // multiple jsxtext:
          // <div>你好,<strong>{userName}</strong>!!!</div>
          // =>
          // <div>{a18n.x`你好,${(<strong>{userName}</strong>)}!!!`}</div>
          case 'JSXText': {
            if (needTranslate(node.value)) {
              const parent = path.parent as t.JSXElement | t.JSXFragment
              const parentPath = path.parentPath as NodePath<
                t.JSXElement | t.JSXFragment
              >
              const isJSXParent =
                t.isJSXElement(parent) || t.isJSXFragment(parent)
              if (!isJSXParent) {
                break
              }

              const hasText = (child: t.Node): boolean =>
                t.isJSXText(child) && child.value.trim() !== ''

              const shouldWrapMultiple =
                parent.children.filter(hasText).length > 1

              if (shouldWrapMultiple) {
                // avoid parent node wrapped multiple times from its jsxtext children,
                // only let the first qualified text node trigger wrapping
                const theOne = parent.children.find(hasText)
                if (theOne && theOne.start !== node.start) {
                  break
                }
                const expressions: Array<t.Expression> = []
                const quasis: Array<t.TemplateElement> = []

                let lastIsText = false
                const ensureLastText = () => {
                  if (!lastIsText) {
                    quasis.push(
                      t.templateElement({
                        raw: '',
                        cooked: '',
                      }),
                    )
                  }
                  lastIsText = true
                }
                ;(parent as t.JSXElement | t.JSXFragment).children.forEach(
                  (child) => {
                    if (t.isJSXText(child)) {
                      if (lastIsText) {
                        const last = quasis[quasis.length - 1]
                        last.value.cooked =
                          (last.value.cooked ?? '') + child.value.trim()
                        last.value.raw = (last.value.cooked ?? '').replace(
                          /\\|`|\${/g,
                          '\\$&',
                        )
                      } else {
                        quasis.push(
                          t.templateElement({
                            raw:
                              // Add a backslash before every ‘`’, ‘\’ and ‘${’.
                              // https://github.com/babel/babel/issues/9242#issuecomment-532529613
                              child.value.trim().replace(/\\|`|\${/g, '\\$&'),
                            cooked: child.value.trim(),
                          }),
                        )
                      }
                      lastIsText = true
                    } else {
                      if (t.isJSXExpressionContainer(child)) {
                        if (t.isJSXEmptyExpression(child.expression)) {
                          console.warn('[wrap] unexpected JSXEmptyExpression', {
                            child,
                            parent,
                          })
                        } else {
                          ensureLastText()
                          expressions.push(child.expression)
                          lastIsText = false
                        }
                      } else if (t.isJSXSpreadChild(child)) {
                        console.warn('[wrap] unexpected JSXSpreadChild', {
                          child,
                          parent,
                        })
                      } else {
                        ensureLastText()
                        expressions.push(child)
                        lastIsText = false
                      }
                    }
                  },
                )
                ensureLastText()

                const children = [
                  t.jsxExpressionContainer(
                    t.taggedTemplateExpression(
                      t.memberExpression(
                        t.identifier(LIB_IDENTIFIER),
                        t.identifier(LIB_METHOD_X_IDENTIFIER),
                      ),
                      t.templateLiteral(quasis, expressions),
                    ),
                  ),
                ]

                if (t.isJSXElement(parent)) {
                  if (checkOnly) {
                    addDynamicText(
                      node,
                      quasis.map((q) => q.value.cooked ?? q.value.raw),
                    )
                  } else {
                    parentPath.replaceWith(
                      t.jsxElement(
                        parent.openingElement,
                        parent.closingElement,
                        children,
                      ),
                    )
                  }
                } else if (t.isJSXFragment(parent)) {
                  if (checkOnly) {
                    addDynamicText(
                      node,
                      quasis.map((q) => q.value.cooked ?? q.value.raw),
                    )
                  } else {
                    parentPath.replaceWith(
                      t.jsxFragment(
                        parent.openingFragment,
                        parent.closingFragment,
                        children,
                      ),
                    )
                  }
                }
              } else {
                const emptyStart = node.value.match(/^\s*/)![0]
                const emptyEnd = node.value.match(/\s*$/)![0]
                const nonEmptyText = node.value.trim()
                if (checkOnly) {
                  addStaticText(node, nonEmptyText)
                } else {
                  path.replaceWithMultiple([
                    t.jsxText(emptyStart),
                    t.jsxExpressionContainer(t.stringLiteral(nonEmptyText)),
                    t.jsxText(emptyEnd),
                  ])
                }
              }

              markLibUsed()
            }
            break
          }

          case 'ImportDeclaration': {
            importStatementCount++

            if (node.source.value === LIB_MODULE) {
              libImportStatus.imported = true
            }
            break
          }

          case 'CallExpression': {
            if (t.isIdentifier(node.callee)) {
              if (node.callee.name === 'require') {
                requireCallExpressionCount++
              }

              const isRequireLibModule =
                node.callee.name === 'require' &&
                fromStringLiteral(node.arguments[0]) === LIB_MODULE
              if (isRequireLibModule) {
                libImportStatus.required = true
              }

              const isFactoryMethod =
                node.callee.name === LIB_FACTORY_IDENTIFIER
              if (isFactoryMethod) {
                const namespace = fromStringLiteral(node.arguments[0])
                libImportStatus.namespace = namespace
              }
            }
            break
          }

          default:
            break
        }
      } catch (error) {
        throw error
      }
    },
  })

  if (checkOnly || !libUsed) {
    return {
      output: code,
      sourceTexts,
    }
  }

  let output: string

  const shouldKeepFactoryStatement =
    (libImportStatus.imported || libImportStatus.required) &&
    libImportStatus.namespace == namespace &&
    newModuleName === existModuleName
  if (shouldKeepFactoryStatement) {
    output = print(ast)
  } else {
    removeImportRequireFactory(ast, lines)
    output = print(ast)

    const shouldUseImport = importStatementCount >= requireCallExpressionCount

    const importDeclarators = namespace
      ? `{ ${LIB_FACTORY_IDENTIFIER} }`
      : LIB_IDENTIFIER
    const importStatement = shouldUseImport
      ? `import ${importDeclarators} from '${LIB_MODULE}'\n`
      : `const ${importDeclarators} = require('${LIB_MODULE}')\n`
    const factoryStatement = namespace
      ? newModuleName
        ? `const a18n = ${LIB_FACTORY_IDENTIFIER}('${namespace}', '${newModuleName}')\n`
        : `const a18n = ${LIB_FACTORY_IDENTIFIER}('${namespace}')\n`
      : ''
    output = importStatement + factoryStatement + output
  }

  return {
    output,
    sourceTexts,
  }
}
Example #21
Source File: plugin.ts    From vidact with MIT License 4 votes vote down vote up
function visitFunction(
  fnPath: NodePath<t.FunctionDeclaration>,
  { moduleDependencies }: ProgramState
) {
  if (!isComponent(fnPath)) {
    return fnPath.skip();
  }

  const variableStatementDependencyManager = new VariableStatementDependencyManager();
  const state: ComponentState = {
    variablesWithDependencies: new Set(),
    needsPropTransaction: false,
    looseAssignments: new Set(),
    state: [],
    variableStatementDependencyManager,
    moduleDependencies,
    finally: [],
  };

  // Separate variable declarations with multiple declarators
  separateVariableDeclarations(fnPath);

  // Rename prop object and move param destruct to body block
  normalizePropDefinition(fnPath);

  // Convert object pattern assignments to identifier assignments
  normalizeObjectPatternAssignment(fnPath);

  // Convert `useRef`s to {current} object style
  normalizeUseRef(fnPath);

  // Convert `useEffect`, `useMemo`, and `useCallback` to proper updaters
  scanHooks(fnPath, state);

  // Traverse all state and prop references
  scanUpdatableValues(fnPath, state);

  // Declare variables defined inside updaters in the function scope
  if (state.variablesWithDependencies.size > 0) {
    fnPath.get("body").unshiftContainer(
      "body",
      t.variableDeclaration(
        "let",
        Array.from(state.variablesWithDependencies).map((name) =>
          t.variableDeclarator(t.identifier(name))
        )
      )
    );
  }

  state.looseAssignments.forEach((path) => {
    declarationToAssignment(path);
  });

  for (const statementPath of variableStatementDependencyManager.statements.values()) {
    if (!hasAnnotation(statementPath.node, "useEffect")) {
      const [updater, callUpdater] = createStatementUpdater(
        statementPath,
        fnPath.scope
      );
      statementPath.replaceWith(updater);
      statementPath.insertAfter(callUpdater);
    }
  }

  const names: t.Identifier[] = [];

  let returnValue: t.Expression;

  fnPath.traverse({
    JSXElement(path) {
      const jsxState: JSXState = {
        elements: [],
        moduleDependencies,
      };

      const name = shallowTraverseJSXElement(path.node, jsxState, path.scope);
      names.push(name);

      jsxState.elements.forEach((definition) => {
        const nodePaths: NodePath[] = [];
        const nodes = transformerMap[definition.type](definition as any, state);

        const nodesList = Array.isArray(nodes) ? nodes : [nodes];

        nodesList.forEach((node, i) => {
          if (node) {
            const parent = path.getStatementParent();
            nodePaths[i] = parent.insertBefore(node)[0];
          }
        });

        if (definition.type === "expr") {
          const nodePath = nodePaths[1];
          getImpactfulIdentifiers(
            definition.expression,
            path.scope,
            path
          ).forEach(([type, name]) => {
            variableStatementDependencyManager.push(
              { type: type as any, name },
              { type: "node", value: nodePath }
            );
          });
        }

        if (definition.type === "node") {
          const { attributes } = definition;
          if (attributes) {
            attributes.forEach((_, i) => {
              const node = nodes[1 + i];
              const nodePath = nodePaths[1 + i];

              const impactfulIds = getImpactfulIdentifiers(
                node,
                path.scope,
                path
              );

              if (impactfulIds.length > 0) {
                const [updater, callUpdater] = createStatementUpdater(
                  nodePath,
                  fnPath.scope
                );
                nodePath.replaceWith(updater);
                nodePath.insertAfter(callUpdater);
              }

              impactfulIds.forEach(([type, name]) => {
                if (name !== definition.identifier.name) {
                  variableStatementDependencyManager.push(
                    { type: type as any, name },
                    { type: "node", value: nodePath }
                  );
                }
              });
            });
          }
        }

        return nodes;
      });

      path.skip();
      if (path.getStatementParent().isReturnStatement()) {
        if (path.scope === fnPath.scope) {
          returnValue = name;
          path.getStatementParent().remove();
        } else {
          path.replaceWith(name);
        }
      } else if (
        !t.isObjectProperty(path.container) &&
        !t.isVariableDeclarator(path.container)
      ) {
        path.getStatementParent().remove();
      } else {
        path.replaceWith(name);
      }
    },
  });

  fnPath.traverse({ VariableDeclaration: scanForDeepDependencies }, state);

  const componentElement = createComponentElement(
    returnValue,
    createUpdatableUpdater(fnPath.get("body"), state, "prop")
  );

  state.moduleDependencies.add("propUpdater");
  const returnPath: NodePath<t.ReturnStatement> = fnPath
    .get("body")
    .pushContainer("body", t.returnStatement(componentElement))[0];

  if (state.needsPropTransaction) {
    fnPath
      .get("body")
      .unshiftContainer(
        "body",
        t.variableDeclaration("const", [
          t.variableDeclarator(
            t.identifier(PROP_VAR_TRANSACTION_VAR),
            t.newExpression(t.identifier("Map"), [])
          ),
        ])
      );
  }

  if (state.state && state.state.length > 0) {
    const [internalStateDeclaration, defineUpdater] = createStateDefinition(
      state,
      fnPath
    );

    fnPath.get("body").unshiftContainer("body", internalStateDeclaration);
    returnPath.insertBefore(defineUpdater);
  }

  if (state.finally.length > 0) {
    returnPath.replaceWith(
      t.tryStatement(
        t.blockStatement([returnPath.node]),
        undefined,
        t.blockStatement(state.finally)
      )
    );
  }

  fnPath.skip();
}
Example #22
Source File: traverse-module.ts    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
// to record `export {a,b,c} from 'd'`

function traverseJsModule(curModulePath: string, callback: (str: string, items: string[], isImport: boolean) => void) {
  const moduleFileContent = fs.readFileSync(curModulePath, {
    encoding: 'utf-8',
  });

  const ast = parser.parse(moduleFileContent, {
    sourceType: 'unambiguous',
    plugins: resolveBabelSyntaxPlugins(curModulePath),
  });

  const collectImportItems = (path: NodePath, subModulePath: string) => {
    const importItems: string[] = [];
    const specifiers = path.get('specifiers');
    if (Array.isArray(specifiers) && specifiers.length) {
      for (let i = 0; i < specifiers.length; i++) {
        const importItem = path.get(`specifiers.${i}`).toString();
        const subItem = `${subModulePath}-${importItem}`;
        if (exportFromMap[subItem]) {
          importItems.push(exportFromMap[subItem]);
        }
        importItems.push(subItem);
      }
    }
    callback(subModulePath, importItems, true);
  };

  traverse(ast, {
    ImportDeclaration(path) {
      // handle syntax `import react from 'react'`
      const node = path.get('source.value');
      const [subModulePath, visited] = moduleResolver(curModulePath, Array.isArray(node) ? '' : node.node.toString());

      if (!subModulePath || visited) {
        collectImportItems(path, subModulePath);
        return;
      }
      traverseModule(subModulePath, callback);
      collectImportItems(path, subModulePath);
    },
    CallExpression(path) {
      if (path.get('callee').toString() === 'require') {
        // handle syntax `const lodash = require('lodash')`
        const [subModulePath, visited] = moduleResolver(
          curModulePath,
          path.get('arguments.0').toString().replace(/['"]/g, ''),
        );
        if (!subModulePath) {
          return;
        }
        callback(subModulePath, [], visited);
        traverseModule(subModulePath, callback);
      }
      if (path.get('callee').type === 'Import') {
        // handle syntax dynamic import `const entry = import('layout/entry')`
        try {
          const importItem = path.get('arguments.0').toString().replace(/['"]/g, '');
          if (importItem && !importItem.includes('`')) {
            const [subModulePath, visited] = moduleResolver(curModulePath, importItem);
            if (!subModulePath || visited) {
              return;
            }
            callback(subModulePath, [], true);
            traverseModule(subModulePath, callback);
          }
        } catch (error) {
          console.log('parse import error', error);
        }
      }
    },
    ExportNamedDeclaration(path) {
      // handle syntax `export { Copy } from './components/copy'`
      if (path.get('source').node) {
        const node = path.get('source.value');
        const [subModulePath, visited] = moduleResolver(
          curModulePath,
          Array.isArray(node) ? '' : node.node.toString().replace(/['"]/g, ''),
        );
        if (!subModulePath || visited) {
          return;
        }
        const specifiers = path.get('specifiers');
        const exportItems: string[] = [];
        if (Array.isArray(specifiers) && specifiers.length) {
          for (let i = 0; i < specifiers.length; i++) {
            const importItem = path.get(`specifiers.${i}`).toString();
            exportFromMap[`${curModulePath}-${importItem}`] = `${subModulePath}-${importItem}`;
            exportItems.push(`${curModulePath}-${importItem}`);
          }
        }
        callback(subModulePath, exportItems, false);
        traverseModule(subModulePath, callback);
      } else if (path.get('declaration').node) {
        // handle export { a, b, c };
        const declarations = path.get('declaration.declarations');
        const exportItems: string[] = [];
        if (Array.isArray(declarations) && declarations.length) {
          for (let i = 0; i < declarations.length; i++) {
            exportItems.push(`${curModulePath}-${path.get(`declaration.declarations.${i}.id`).toString()}`);
          }
        }
        callback('', exportItems, false);
      }
    },
  });
}
Example #23
Source File: babelPlugin.ts    From i18n-helper with MIT License 4 votes vote down vote up
i18nPlugin = (transInfo: iTransInfo, i18nConf: iI18nConf): any => {
  const JSX_WRAPPER = 'trans';
  const OPERATORS = ['===', '==', '!==', '!='];
  const { wrapCharacter, wrapperFuncName: T_WRAPPER } = i18nConf;

  /**
   * 获取 MemberExpression 完整名字
   * @param ME MemberExpression
   * @param names Array
   */
  const getName = (ME: tt.MemberExpression, names: string[]) => {
    let { property } = ME;
    const { object } = ME;
    property = property as tt.Identifier;
    names.push(property.name);
    if (object.type === 'MemberExpression') {
      getName(object, names);
    } else if (object.type === 'Identifier') {
      names.push((object as tt.Identifier).name);
    }
  };
  const plugin = ({ types: t }: { types: any }) => {
    const combine = (value: string) =>
      Object.assign(t.StringLiteral(value), {
        extra: {
          raw: `\'${value.replace("'", "\\'")}\'`,
          rawValue: value,
        },
      });

    const replaceLineBreak = (value: string) => {
      return value.replace(/\n/g, ' ').trim();
    };

    const collectWordingInfo = (
      value: string,
      path: NodePath<tt.Node>,
      thisArg: any,
      list: iWordInfo[],
    ) => {
      const filename =
        thisArg.filename || thisArg.file.opts.filename || 'unknown';
      const line = path.node.loc?.start?.line ?? 0;

      const wordInfo: iWordInfo = {
        key: value,
        filename,
        line,
      };

      // wordInfo 结构
      // [
      //   { key: '武强', fileName: 'index.js', line: '91' },
      //   { key: '你好呀', fileName: 'index.js', line: '191' },
      //   { key: '武强', fileName: 'index.ts', line: '291' },
      // ];
      list.push(wordInfo);
    };

    return {
      visitor: {
        StringLiteral(path: NodePath<tt.StringLiteral>) {
          let { value } = path.node;
          value = replaceLineBreak(value);
          if (needWrap(wrapCharacter, value)) {
            // console.log(`string直接用 replaceLineBreak value:${value}`);
            let newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
              combine(value),
            ]);

            if (path.parentPath?.node.type === 'JSXAttribute') {
              newNode = t.JSXExpressionContainer(newNode);
            }
            path.replaceWith(newNode);

            transInfo.needT = true;
            transInfo.wrapCount += 1;
            collectWordingInfo(
              value,
              path as NodePath,
              this,
              transInfo.wordInfoArray,
            );
          }
        },

        TemplateLiteral(path: NodePath<tt.TemplateLiteral>) {
          // 全部不包含中文词条则不处理
          if (
            path.node.quasis.every(
              (word) => !needWrap(wrapCharacter, word.value.raw),
            )
          ) {
            return;
          }

          const quasisExpressionsList = (
            [].concat(
              path.node.quasis as [],
              path.node.expressions as [],
            ) as TLQuasisExpressions
          ).sort((a, b) => {
            const aStart = a.start ?? 0;
            const bStart = b.start ?? 0;
            return aStart - bStart;
          });

          let value = '';
          let CEIndex = 0;
          let BEIndex = 0;
          let LEIndex = 0;
          let OMEIndex = 0;
          let OCEIndex = 0;
          const variableList: any = [];

          // 组装模板字符串左侧部分
          quasisExpressionsList.forEach((templateLiteralItem) => {
            const variable: any = {};
            switch (templateLiteralItem.type) {
              case 'TemplateElement':
                // 文字
                value += `${replaceLineBreak(
                  templateLiteralItem.value.cooked ?? '',
                )}`;
                break;
              case 'Identifier': {
                // `我有{xx}`
                const identifierName = templateLiteralItem.name;

                variable.type = 'Identifier';
                variable.key = identifierName;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${identifierName}}}`;
                break;
              }
              case 'CallExpression': {
                // `我有{obj.xx()}`
                // `我有{xx()}`

                const { type } = templateLiteralItem.callee;
                let callExpressionName = '';
                if (type === 'Identifier') {
                  callExpressionName = (
                    templateLiteralItem.callee as tt.Identifier
                  ).name;
                } else if (type === 'MemberExpression') {
                  callExpressionName = (
                    (templateLiteralItem.callee as tt.MemberExpression)
                      .property as tt.Identifier
                  ).name;
                }

                variable.type = 'CallExpression';
                variable.key = callExpressionName;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${callExpressionName}}}`;
                break;
              }
              case 'MemberExpression': {
                let memberExpressionName;
                // `我有${obj.xx},${obj[xx]}` Identifier
                // `我有${obj['xx']}` StringLiteral
                // `我有${array[0]}` NumericLiteral
                switch (templateLiteralItem.property.type) {
                  case 'Identifier':
                    memberExpressionName = (
                      templateLiteralItem.property as tt.Identifier
                    ).name;
                    break;
                  case 'StringLiteral':
                    memberExpressionName = (
                      templateLiteralItem.property as tt.StringLiteral
                    ).value;
                    break;
                  case 'NumericLiteral':
                    memberExpressionName = (
                      templateLiteralItem.property as tt.NumericLiteral
                    ).value.toString();
                    break;
                  case 'MemberExpression':
                    memberExpressionName = (
                      templateLiteralItem.property.property as tt.Identifier
                    ).name;
                    break;
                  case 'BinaryExpression':
                    // TODO: 需要看看怎么改
                    memberExpressionName = 'BinaryExpression';
                    break;
                  default:
                    break;
                }

                variable.type = 'MemberExpression';
                variable.key = memberExpressionName;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${memberExpressionName}}}`;
                break;
              }
              case 'ConditionalExpression': {
                // TODO: 目前用ConditionalExpression{index}作为key,可读性不是太好
                // 另外如果`武强${index > '这' ? '哈哈' : '呵呵'}呢`
                // 如上'这',’哈哈‘,’呵呵‘都不会被包裹。。。
                CEIndex += 1;
                variable.type = 'ConditionalExpression';
                variable.key = `ConditionalExpression${CEIndex}`;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${variable.key}}}`;
                break;
              }
              case 'BinaryExpression': {
                BEIndex += 1;
                variable.type = 'BinaryExpression';
                variable.key = `BinaryExpression${BEIndex}`;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${variable.key}}}`;
                break;
              }
              case 'LogicalExpression': {
                LEIndex += 1;
                variable.type = 'LogicalExpression';
                variable.key = `LogicalExpression${LEIndex}`;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${variable.key}}}`;
                break;
              }
              case 'OptionalMemberExpression': {
                OMEIndex += 1;
                variable.type = 'OptionalMemberExpression';
                variable.key = `OptionalMemberExpression${OMEIndex}`;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${variable.key}}}`;
                break;
              }
              case 'OptionalCallExpression': {
                OCEIndex += 1;
                variable.type = 'OptionalCallExpression';
                variable.key = `OptionalCallExpression${OCEIndex}`;
                variable.value = templateLiteralItem;
                variableList.push(variable);

                value += `{{${variable.key}}}`;
                break;
              }
              default: {
                const thisArgs = this as any;
                const filename =
                  thisArgs.filename || thisArgs.file.opts.filename || 'unknown';
                const line = path.node.loc?.start?.line ?? 0;
                Logger.appendFile(
                  `[${filename}][${line}]: ${templateLiteralItem.type} 未处理`,
                );
                transInfo.wrapSuccess = false;
                break;
              }
            }
          });

          // 组装模板字符串右侧对象
          const objArray: any = [];
          variableList.map((item: any) => {
            let obj;
            switch (item.type) {
              case 'Identifier': {
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.Identifier,
                );
                obj.shorthand = true;
                break;
              }
              case 'CallExpression':
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.CallExpression,
                );
                break;
              case 'MemberExpression':
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.MemberExpression,
                );
                break;
              case 'ConditionalExpression':
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.ConditionalExpression,
                );
                break;
              case 'BinaryExpression':
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.BinaryExpression,
                );
                break;
              case 'LogicalExpression':
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.LogicalExpression,
                );
                break;
              case 'OptionalMemberExpression':
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.OptionalMemberExpression,
                );
                break;
              case 'OptionalCallExpression':
                obj = t.objectProperty(
                  t.Identifier(item.key),
                  item.value as tt.OptionalCallExpression,
                );
                break;
              default:
                break;
            }
            objArray.push(obj);
          });

          let newNode;
          if (objArray.length > 0) {
            newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
              combine(value),
              t.ObjectExpression(objArray),
            ]);
          } else {
            // 处理文字全用``包裹的,并没有${}的内容,如 const word = `你好`
            newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
              combine(value),
            ]);
          }
          path.replaceWith(newNode);

          transInfo.needT = true;
          transInfo.wrapCount += 1;
          collectWordingInfo(
            value,
            path as NodePath,
            this,
            transInfo.wordInfoArray,
          );
        },

        JSXText(path: NodePath<tt.JSXText>) {
          const { node } = path;
          const { value } = node;

          // jsx内部文字包含中文且不被<trans></trans>包裹
          if (
            needWrap(wrapCharacter, value)
            // needWrap(value) &&
            // (
            //   (path.parentPath.node as tt.JSXElement).openingElement
            //     .name as tt.JSXIdentifier
            // ).name !== JSX_WRAPPER
          ) {
            let newNode;
            if (i18nConf.jsx2Trans) {
              // 用trans包裹
              newNode = t.jsxElement(
                t.jsxOpeningElement(t.jsxIdentifier(JSX_WRAPPER), []),
                t.jsxClosingElement(t.jsxIdentifier(JSX_WRAPPER)),
                [t.jsxText(value)],
                true,
              );
            } else {
              // 用 t 包裹
              // console.log(`jsx直接用value:${value}`);
              newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
                combine(replaceLineBreak(value)),
              ]);
              newNode = t.JSXExpressionContainer(newNode);
            }
            path.replaceWith(newNode);

            transInfo.needTrans = true;
            transInfo.wrapCount += 1;
            collectWordingInfo(
              replaceLineBreak(node.value.trim()),
              path as NodePath,
              this,
              transInfo.wordInfoArray,
            );
          }
        },

        CallExpression(path: NodePath<tt.CallExpression>) {
          const excludeFuncNameRegex =
            i18nConf.parsedExcludeWrapperFuncNameRegex;
          switch (path.node.callee.type) {
            // TODO: 这里目前只能处理 Identifier 方法,后续需要修改
            case 'Identifier': {
              const { name } = path.node.callee as tt.Identifier;

              // 如果是 i18n 包裹的则只收集词条
              if (name === T_WRAPPER) {
                path.node.arguments
                  .filter((arg) => arg.type === 'StringLiteral')
                  .map((sl) => {
                    const node = sl as tt.StringLiteral;
                    collectWordingInfo(
                      node.value.trim(),
                      path as NodePath,
                      this,
                      transInfo.wordInfoArray,
                    );
                  });
                path.skip();
              }

              // 处理排除方法 excludeWrapperFuncName
              if (excludeFuncNameRegex.test(name)) {
                path.skip();
              }
              break;
            }
            case 'MemberExpression': {
              // 处理排除方法 excludeWrapperFuncName
              const names: string[] = [];
              const me = path.node.callee as tt.MemberExpression;
              getName(me, names);
              const MEName = names.reverse().join('.');
              if (excludeFuncNameRegex.test(MEName)) {
                path.skip();
              }

              break;
            }
            default:
              break;
          }
        },

        BinaryExpression(path: NodePath<tt.BinaryExpression>) {
          if (OPERATORS.includes(path.node.operator)) {
            path.skip();
          }
        },

        ImportDeclaration(path: NodePath<tt.ImportDeclaration>) {
          if (path.node.source.extra?.raw === i18nConf.parsedImportKey) {
            transInfo.needImport = false;
          }
        },
      },
    };
  };

  return plugin;
}