Repository: babel/babel
File: packages/babel-traverse/src/path/modification.ts (TypeScript)

Code before:
 */

export function insertBefore<Nodes extends NodeOrNodeList<t.Node>>(
  this: NodePath,
  nodes_: Nodes,
): NodePaths<Nodes> {
  _assertUnremoved.call(this);

  const nodes = _verifyNodeList.call(this, nodes_);

  const { parentPath, parent } = this;

  if (
    parentPath.isExpressionStatement() ||
    parentPath.isLabeledStatement() ||
    // https://github.com/babel/babel/issues/15293
    // When Babel transforms `export class String { field }`, the class properties plugin will inject the defineProperty
    // helper, which depends on the builtins e.g. String, Number, Symbol, etc. To prevent them from being shadowed by local
    // exports, the helper injector replaces the named export into `class _String { field }; export { _String as String }`,
    // with `parentPath` here changed to the moved ClassDeclaration, causing rare inconsistency between `parent` and `parentPath`.
    // Here we retrieve the parent type from the `parent` property. This is a temporary fix and we should revisit when
    // helpers should get injected.
    isExportNamedDeclaration(parent) ||
    (parentPath.isExportDefaultDeclaration() && this.isDeclaration())
  ) {
    return parentPath.insertBefore(nodes) as NodePaths<Nodes>;
  } else if (
    (this.isNodeType("Expression") && !this.isJSXElement()) ||
    (parentPath.isForStatement() && this.key === "init")
  ) {
    if (this.node) nodes.push(this.node);
    // @ts-expect-error todo(flow->ts): check that nodes is an array of statements
    return this.replaceExpressionWithStatements(nodes);
  } else if (Array.isArray(this.container)) {
    return _containerInsertBefore.call(this, nodes) as NodePaths<Nodes>;
  } else if (this.isStatementOrBlock()) {
    const node = this.node as t.Statement;
    const shouldInsertCurrentNode =
      node &&
      (!this.isExpressionStatement() ||
        (node as t.ExpressionStatement).expression != null);

    const [blockPath] = this.replaceWith(
      blockStatement(shouldInsertCurrentNode ? [node] : []),
    );
    return blockPath.unshiftContainer(
      "body",
      nodes as t.Statement[],
    ) as NodePaths<Nodes>;
  } else {
    throw new Error(

Review comment (suggestion):
Would this work instead of the type case?
```suggestion
    return parentPath.insertBefore<Nodes>(nodes);
```

Code after:
 */

export function insertBefore<Nodes extends NodeOrNodeList<t.Node>>(
  this: NodePath,
  nodes_: Nodes,
): NodePaths<Nodes> {
  _assertUnremoved.call(this);

  const nodes = _verifyNodeList.call(this, nodes_);

  const { parentPath, parent } = this;

  if (
    parentPath.isExpressionStatement() ||
    parentPath.isLabeledStatement() ||
    // https://github.com/babel/babel/issues/15293
    // When Babel transforms `export class String { field }`, the class properties plugin will inject the defineProperty
    // helper, which depends on the builtins e.g. String, Number, Symbol, etc. To prevent them from being shadowed by local
    // exports, the helper injector replaces the named export into `class _String { field }; export { _String as String }`,
    // with `parentPath` here changed to the moved ClassDeclaration, causing rare inconsistency between `parent` and `parentPath`.
    // Here we retrieve the parent type from the `parent` property. This is a temporary fix and we should revisit when
    // helpers should get injected.
    isExportNamedDeclaration(parent) ||
    (parentPath.isExportDefaultDeclaration() && this.isDeclaration())
  ) {
    return parentPath.insertBefore(nodes as Nodes);
  } else if (
    (this.isNodeType("Expression") && !this.isJSXElement()) ||
    (parentPath.isForStatement() && this.key === "init")
  ) {
    if (this.node) nodes.push(this.node);
    // @ts-expect-error todo(flow->ts): check that nodes is an array of statements
    return this.replaceExpressionWithStatements(nodes);
  } else if (Array.isArray(this.container)) {
    return _containerInsertBefore.call(this, nodes) as NodePaths<Nodes>;
  } else if (this.isStatementOrBlock()) {
    const node = this.node as t.Statement;
    const shouldInsertCurrentNode =
      node &&
      (!this.isExpressionStatement() ||
        (node as t.ExpressionStatement).expression != null);

    const [blockPath] = this.replaceWith(
      blockStatement(shouldInsertCurrentNode ? [node] : []),
    );
    return blockPath.unshiftContainer(
      "body",
      nodes as t.Statement[],
    ) as NodePaths<Nodes>;
  } else {
    throw new Error(