As an example, write an auto-generated API document

Call the plugin

// index.js
const { transformFromAstSync } = require('@babel/core');
const  parser = require('@babel/parser');
const autoDocumentPlugin = require('./plugin/auto-document-plugin');
const fs = require('fs');
const path = require('path');

// Read the file
const sourceCode = fs.readFileSync(path.join(__dirname, './sourceCode.ts'), {
    encoding: 'utf-8'
});

/ / to AST
const ast = parser.parse(sourceCode, {
    sourceType: 'unambiguous'.plugins: ['typescript']});// Plugins are used
const { code } = transformFromAstSync(ast, sourceCode, {
    plugins: [[autoDocumentPlugin, {
        outputDir: path.resolve(__dirname, './docs'),
        format: 'markdown'// html / json}}]]);Copy the code

Add-in template

// auto-document-plugin.js
const { declare } = require('@babel/helper-plugin-utils');
const fse = require('fs-extra');
const path = require('path');
const autoDocumentPlugin = declare((api, options, dirname) = > {
    api.assertVersion(7);

    return {
        pre(file) {
            file.set('docs'[]); },// Visitor, the most critical place
        visitor: {
            FunctionDeclaration(path, state) {
                // const docs = state.file.get('docs');
                // docs.push({
                // type: 'function',
                // name: path.get('id').toString(),
                // params: path.get('params').map(paramPath=> {
                // return {
                // name: paramPath.toString(),
                // type: resolveType(paramPath.getTypeAnnotation())
                / /}
                / /}),
                // return: resolveType(path.get('returnType').getTypeAnnotation()),
                // doc: path.node.leadingComments && parseComment(path.node.leadingComments[0].value)
                // });
                // state.file.set('docs', docs);}},post(file) {
            const docs = file.get('docs');
            const res = generate(docs, options.format);
            fse.ensureDirSync(options.outputDir);
            fse.writeFileSync(path.join(options.outputDir, 'docs'+ res.ext), res.content); }}});Copy the code

The visitor pattern

Visitor pattern (Visitor pattern) as one of the design patterns. The visitor pattern addresses the coupling between data and the way it operates, making the way it operates independent of the data and allowing it to evolve freely. So access is more suitable for environments where the data is stable, but the data is operated on once. Therefore, when the operator environment changes, the operation method can be modified freely to adapt to the operation environment, without modifying the original data, and the expansion of the operation method can be realized.

With the implementation of Babel traverse, the AST(data) is separated from the visitor (which has many operations), and the registered visitor is called to handle the AST while it traverses it

Path and scope

path:NodePath
Copy the code

The Babel AST contains only some information about the source code, but to operate on the AST you need to get information about the parent node, and you also need to add, delete, or modify the AST, which is in the path object.

Scope is scope information, javascript can generate scope is module, function, block, etc., and the nested relationship between scopes will be formed, that is, scope chain. Babel generates a chain of scopes that is stored in path.scope as it traverses.

Check out the Babel website for a few plugins

babel-plugin-proposal-export-default-from

babel-plugin-proposal-function-bind

babel-plugin-proposal-numeric-separator

The Path API is too powerful to be familiar with

@babel/types

@babel/types

conclusion

A general idea of how to write a Babel plug-in that, after transforming into an AST, applies the visitor pattern of design mode (decoupled data from data manipulation methods) in the Transform phase, and then implements the logic through the PATH API. The type of path is NodePath, which contains the AST path and scope (scope chain), through various operations on the API, to achieve the results we want

visitor: { 
ExportNamedDeclaration(path) { 
  const { node, scope } = path;
}
Copy the code

reference

Gold digger Babel plug-in to complete the secret