Skip to content

Helpers and builders

vue-metamorph provides a set of helper functions and builder functions through the utils object that it passes to the transform() or find() function of your plugin.

ts
const myCodemod: CodemodPlugin = {
  type: 'codemod',
  name: 'my-codemod',
  transform({ scriptASTs, sfcAST, utils }) {
    // utils.astHelpers   - functions for finding nodes and managing imports
    // utils.builders     - functions for creating new AST nodes
  }
}

AST helpers

findFirst

Finds the first node in an AST that matches a partial object. This function works with both script ASTs and template ASTs.

ts
const { astHelpers } = utils;

// find the first <div> in the template
const div = astHelpers.findFirst(sfcAST, {
  type: 'VElement',
  name: 'div',
});

// find the first call to `console.log` in a script
const consoleLog = astHelpers.findFirst(scriptAST, {
  type: 'CallExpression',
  callee: {
    type: 'MemberExpression',
    object: { type: 'Identifier', name: 'console' },
    property: { type: 'Identifier', name: 'log' },
  },
});

Returns the first matching node, or null if there's no match.

findAll

Finds every node in an AST that matches a partial object.

ts
const { astHelpers } = utils;

// find all <custom> elements in the template
const elements = astHelpers.findAll(sfcAST, {
  type: 'VElement',
  name: 'custom',
});

// find all call expressions in a script
const calls = astHelpers.findAll(scriptAST, {
  type: 'CallExpression',
});

Returns an array of all the matching nodes.

findImportDeclaration

Finds an existing import declaration for a module.

ts
const { astHelpers } = utils;

const vueImport = astHelpers.findImportDeclaration(scriptAST, 'vue');

if (vueImport) {
  // an import from 'vue' exists
}

Returns the ImportDeclaration node, or null if there's no match.

createNamedImport

Adds a named import to a script AST. If an import declaration for the module already exists, this function adds the new specifier to that declaration. It doesn't create duplicate imports.

ts
const { astHelpers } = utils;

// import { defineComponent } from 'vue';
astHelpers.createNamedImport(scriptAST, 'vue', 'defineComponent');

// import { map as lodashMap } from 'lodash-es';
astHelpers.createNamedImport(scriptAST, 'lodash-es', 'map', 'lodashMap');

createDefaultImport

Adds a default import to a script AST. This function follows the same merging and deduplication logic as createNamedImport.

ts
const { astHelpers } = utils;

// import Vue from 'vue';
astHelpers.createDefaultImport(scriptAST, 'vue', 'Vue');

createNamespaceImport

Adds a namespace import to a script AST. This function follows the same merging and deduplication logic as createNamedImport.

ts
const { astHelpers } = utils;

// import * as _ from 'lodash-es';
astHelpers.createNamespaceImport(scriptAST, 'lodash-es', '_');

findVueComponentOptions

Finds every Options API object expression in a script. This function detects defineComponent(), Vue.extend(), Vue.component(), Vue.mixin(), and new Vue(). When isSfc is true, it also detects the default export.

ts
const { astHelpers } = utils;

for (const scriptAST of scriptASTs) {
  const optionsObjects = astHelpers.findVueComponentOptions(scriptAST, sfcAST !== null);

  for (const obj of optionsObjects) {
    // obj is an ObjectExpression node — the { ... } passed to defineComponent(), etc.
  }
}

Template builders

The utils.builders object includes functions that create new template AST nodes. Use them when your codemod needs to insert new elements, attributes, or directives into the <template>.

TIP

After you build new nodes and insert them into the AST, call builders.setParents() on the root of the new subtree. Builder functions leave parent references unset, and setParents() fills them in.

ts
const newElement = builders.vElement('div', builders.vStartTag([], false), []);
builders.setParents(sfcAST); // fix parent references

vElement

Creates a new element node. This function creates an end tag automatically, unless the tag is self-closing or a void element.

ts
const { builders } = utils;

const div = builders.vElement(
  'div',
  builders.vStartTag([
    builders.vAttribute(builders.vIdentifier('class'), builders.vLiteral('container')),
  ], false),
  [builders.vText('Hello')],
);
// <div class="container">Hello</div>

vStartTag

Creates a start tag with attributes, directives, or both.

ts
const { builders } = utils;

const startTag = builders.vStartTag(
  [
    builders.vAttribute(builders.vIdentifier('id'), builders.vLiteral('app')),
  ],
  false, // selfClosing
);

vAttribute

Creates a static attribute.

ts
const { builders } = utils;

// class="active"
const attr = builders.vAttribute(
  builders.vIdentifier('class'),
  builders.vLiteral('active'),
);

// disabled (boolean attribute, no value)
const disabled = builders.vAttribute(
  builders.vIdentifier('disabled'),
  null,
);

vDirective

Creates a Vue directive. Note that a VDirective node has the AST type 'VAttribute' with directive: true.

ts
const { builders } = utils;

// v-if="visible"
const vIf = builders.vDirective(
  builders.vDirectiveKey(builders.vIdentifier('if')),
  builders.vExpressionContainer(builders.identifier('visible')),
);

// :key="item.id"
const vBindKey = builders.vDirective(
  builders.vDirectiveKey(
    builders.vIdentifier('bind', ':'),
    builders.vIdentifier('key'),
  ),
  builders.vExpressionContainer(
    builders.memberExpression(
      builders.identifier('item'),
      builders.identifier('id'),
    ),
  ),
);

vDirectiveKey

Creates the key part of a directive: v-name:argument.modifier1.modifier2.

ts
const { builders } = utils;

// v-on:click.prevent
const key = builders.vDirectiveKey(
  builders.vIdentifier('on'),
  builders.vIdentifier('click'),
  [builders.vIdentifier('prevent')],
);

vExpressionContainer

Creates an expression container, which is either in text or a directive value.

ts
const { builders } = utils;

// {{ message }}
const interpolation = builders.vExpressionContainer(
  builders.identifier('message'),
);

vText

Creates a text node.

ts
const { builders } = utils;

const text = builders.vText('Hello, world!');

vIdentifier

Creates an identifier node for an attribute name, a directive name, a directive argument, or a directive modifier. The optional rawName parameter controls what vue-metamorph prints, which is useful for directive shorthands.

ts
const { builders } = utils;

builders.vIdentifier('class');          // prints: class
builders.vIdentifier('bind', ':');      // name is 'bind', prints as ':'
builders.vIdentifier('on', '@');        // name is 'on', prints as '@'

vLiteral

Creates a string literal node for a static attribute value.

ts
const { builders } = utils;

builders.vLiteral('my-class'); // used with vAttribute for: class="my-class"

htmlComment

Creates an HTML comment node. To attach the comment to another node, set the leadingComment property of that node.

ts
const { builders } = utils;

const comment = builders.htmlComment('TODO: refactor this');
const text = builders.vText('Hello', comment);
// <!-- TODO: refactor this -->Hello

setParents

Traverses a node tree and sets the parent property on every descendant. Call this function after you build new nodes and insert them.

ts
const { builders } = utils;

// after inserting new nodes into sfcAST:
builders.setParents(sfcAST);

Script builders

The utils.builders object also includes all the script AST builders from ast-types. Use them to create JavaScript and TypeScript AST nodes.

ts
const { builders } = utils;

// 'hello'
builders.literal('hello');

// myVariable
builders.identifier('myVariable');

// a + b
builders.binaryExpression('+', builders.identifier('a'), builders.identifier('b'));

// myFunction()
builders.callExpression(builders.identifier('myFunction'), []);

For the full list of available builders, see the ast-types documentation. To see the node shapes that these builders need to produce, use the Playground.