Babel write a plug-rich content to your console

console.log I believe many people have used, as usually work in the main debugging tool, I often somewhat confused, is how to find the information corresponding to the console print source. Normally some string will be added prior to printing information, as follows:

console.log('from handleFileUpload---->', data);
复制代码

Is there a better way to meet this demand? The best information is automatically added. Because recent studies ast, so why not wanted by Babel in the process of compiling the source code, add the information I wanted to do automatic parameter console, and thus there is this plug-in.

Basic information of Babel I will not describe here, there is a lot of information about Babel great, but the main focus of this paper describes the principles to achieve this plug-in.

Note: babel-Handbook is highly recommended, Babel of maintenance.

First, the information needs to be added: the current file name where the console, the execution context / call stack, the number of rows, columns, user-defined information.

Second, also need to be clear and specific approach to console which entered into force, such as the entry into force only for console.log, and console.warn not take effect. Default widget console method into effect comprises 'debug', 'error', 'exception', 'info', 'log', 'warn'.

Finally, the need to exclude some files, such as dependence node_modules.

Now that demand has made it clear, then the rest of the code that corresponds to the question of how to achieve a.

Where the information needs to be added in, where the file name can be obtained from the execution context Babel plug in the filename property, the number of columns rows can node in Babel conversion of AST in obtaining, user-defined content can be served out by the plug-in configuration interface obtain.

Get where the call stack is relatively cumbersome, the general call AST node stack would include a function declaration, variable declarations, object properties, object methods, class methods, etc., for example, the following code:

class Foo {
  bar() {
    const help = () => {
      console.info('banana');
    }
  }
}
复制代码

console.info('banana')Calculating call stack upwardly, respectively, Help function declaration node, node bar class methods, class Foo. We can look up through findParent path of the parent meet the type condition, and get them name.

Note: Each node has a node corresponding to the path Babel, list structure, all nodes in series from the list by AST tree, and similar relations React in the ReactElement and Fiber.

Related code is as follows, satisfy the conditions up to recursive node name into an array:

const scopeHandlers = {
  FunctionDeclaration: path => `${path.node.id.name}()`,
  VariableDeclarator: path => path.node.id.name,
  ObjectProperty: path => path.node.key.name,
  ObjectMethod: path => `${path.node.key.name}()`,
  ClassMethod: path => `${path.node.key.name}()`,
  ClassExpression: path => path.node.id.name,
  ClassDeclaration: path => path.node.id.name,
  AssignmentExpression: path => path.node.left.name
};

export function computeContext(path, scope = []) {
  const parentPath = path.findParent(path =>
    Object.keys(scopeHandlers).includes(path.type)
  );
  if (parentPath) {
    return computeContext(parentPath, [
      scopeHandlers[parentPath.type](parentPath),
      ...scope
    ]);
  }
  return scope.length ? `${scope.join(' -> ')}` : '';
}
复制代码

As for restrictions only console Methods section file to take effect, or only console in a very simple method to take effect, only parameter needs to be done to check the front of the plug-in method.

Such a simple plug on the realization of Babel, the main code is as follows:

import computeOptions from './utils/pluginOption';
import { isObject, matchesExclude, computeContext } from './utils/tools';

export default function({ types: t }) {
  const visitor = {
    CallExpression(path) {
      if (
        t.isMemberExpression(path.node.callee) &&
        path.node.callee.object.name === 'console'
      ) {
        // options need to be an object
        if (this.opts && !isObject(this.opts)) {
          return console.error(
            '[babel-plugin-console-enhanced]: options need to be an object.'
          );
        }

        const options = computeOptions(this.opts);

        const filename = this.filename || this.file.opts.filename || 'unknown';

        // not work on an excluded file
        if (
          Array.isArray(options.exclude) &&
          options.exclude.length &&
          matchesExclude(options.exclude, filename)
        ) {
          return;
        }

        // not work on a non-inlcuded method
        if (!options.methods.includes(path.node.callee.property.name)) {
          return;
        }

        let description = '';

        if (options.addFilename) {
          description = `${description}filename: ${filename}, `;
        }

        if (options.addCodeLine) {
          const line = path.node.loc.start.line;
          description = `${description}line: ${line}, `;
        }

        if (options.addCodeColumn) {
          const column = path.node.loc.start.column;
          description = `${description}column: ${column}, `;
        }

        if (options.addContext) {
          const scope = computeContext(path);
          description = scope
            ? `${description}context: ${scope}, `
            : description;
        }

        if (options.customContent) {
          description = `${description}${options.customContent}, `;
        }

        if (description) {
          path.node.arguments.unshift(t.stringLiteral(description));
        }
      }
    }
  };

  return {
    name: 'babel-plugin-console-enhanced',
    visitor
  };
}
复制代码

Related code base: github.com/mcuking/bab...

Another recent'm writing a compiler React Vue code into the code converter, we welcome the review.

github.com/mcuking/vue…

Reproduced in: https: //juejin.im/post/5d06050151882518e845cca4

Guess you like

Origin blog.csdn.net/weixin_33804582/article/details/93173116