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.
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.
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.
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.
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. It reuses a declaration that has no namespace specifiers, or creates a separate declaration if needed. Repeated calls with the same exported name and local alias don't add another binding.
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. It reuses the first declaration for the module and skips the addition if that declaration already has the same default binding. It throws an error if that declaration has a different default binding.
const { astHelpers } = utils;
// import Vue from 'vue';
astHelpers.createDefaultImport(scriptAST, 'vue', 'Vue');createNamespaceImport
Adds a namespace import to a script AST. If the first declaration for the module has named imports, it creates a separate declaration. If that declaration already has a namespace binding, it skips an identical binding or throws an error for a different name.
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(options), Vue.extend(options), Vue.component(name, options), Vue.mixin(options), and new Vue(options). When isSfc is true, it also detects the default export.
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>.
Builder functions leave parent references unset. After inserting nodes, call builders.setParents(sfcAST) if your plugin needs to read parent references. vue-metamorph also sets parent references before printing.
if (sfcAST) {
const newElement = builders.vElement('div', builders.vStartTag([], false), []);
sfcAST.children.push(newElement);
builders.setParents(sfcAST);
}vElement
Creates a new element node. This function creates an end tag automatically, unless the tag is self-closing or a void element.
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.
const { builders } = utils;
const startTag = builders.vStartTag(
[
builders.vAttribute(builders.vIdentifier('id'), builders.vLiteral('app')),
],
false, // selfClosing
);vAttribute
Creates a static attribute.
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.
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.
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.
const { builders } = utils;
// {{ message }}
const interpolation = builders.vExpressionContainer(
builders.identifier('message'),
);vText
Creates a text node.
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.
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.
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.
const { builders } = utils;
const comment = builders.htmlComment('TODO: refactor this');
const text = builders.vText('Hello', comment);
// <!-- TODO: refactor this -->HellosetParents
Traverses a node tree and sets the parent property on every descendant. Call this function after you build new nodes and insert them.
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.
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.
