javascriptjscodeshift

How to wrap all toplevel statements and declarations except imports in jscodeshift?


I have this AST explorer snippet which almost lets me do what I want, which is to turn this:

function bar() {return 42;}
var baz = {}
import 'get-outta-here';

into

import 'get-outta-here';

function wrap() {
  function bar() {return 42;}
  var baz = {}
}();

i.e. wrap all top-level statements and declarations except the import ones. However, my jscodeshift transform is buggy in a way I can't understand.

I suspect my wrapping logic is off: root.get().value.program.body sounds hacky.

export default function transformer(file, api) {
  const j = api.jscodeshift;
  const root = j(file.source);

  const wrapper = j(j.expressionStatement(
        j.callExpression(
          j.functionExpression(
            j.identifier('foo'), [],
            j.blockStatement(root.get().value.program.body)
          ), []), []));
  
  const nodes = root.find(j.ImportDeclaration).nodes();
  root.find(j.ImportDeclaration).remove();

  // wraps, but doesn't re-add the import at top-level
  return wrapper.toSource();
  
  // fails
  // return wrapper.find(j.Statement).at(0).insertBefore(nodes).toSource();
  
  // moves it to the beginning, but no wrap
  return root.find(j.Statement).at(0).insertBefore(nodes).toSource();
}

Solution

  • Got it. Just use j.program to put a proper "Program" AST in wrapper.

    export default function transformer(file, api) {
      const j = api.jscodeshift;
      const root = j(file.source);
    
      const wrapper = j(
        j.program([
          j.expressionStatement(
            j.callExpression(
              j.functionExpression(
                j.identifier("foo"),
                [],
                j.blockStatement(root.get().value.program.body)
              ),
              []
            ),
            []
          )
        ])
      );
    
      const imports = root.find(j.ImportDeclaration);
      const nodes = imports.nodes();
      imports.remove();
      return wrapper.find(j.Statement).at(0).insertBefore(nodes).toSource();
    }