One, foreword

The main features of webPack are as follows:

  • Load code blocks asynchronously
  • Extract common code blocks (Commons)/Vendors libraries (Vendors)
  • Loader compilation

Ii. Webpack workflow

The running flow of Webpack is a sequential process, from start to finish:

  • Initialization parameters: read and merge parameters from configuration files and Shell statements to get the final parameters;
  • Start compiling: initialize the Compiler object using the parameters obtained in the previous step, load all the configured plug-ins, and execute the object’s run method to start compiling.
  • Determine entry: find all entry files according to the entry in the configuration;
  • Compiling module: starting from the entry file, call all configured Loader to compile the module, find out the module that the module depends on, and then recurse this step until all the entry dependent files have gone through this step;
  • Complete module compilation: After compiling all modules using Loader, the compiled final content of each module and the dependencies between them are obtained;
  • Output resources: assemble chunks containing multiple modules one by one according to the dependency between the entry and modules, and then convert each Chunk into a separate file and add it to the output list. This step is the last chance to modify the output content.
  • Output complete: After determining the output content, determine the output path and file name based on the configuration, and write the file content to the file system.
  • In the above process, Webpack will broadcast a specific event at a specific point in time, the plug-in will execute a specific logic after listening for the event of interest, and the plug-in can call the API provided by Webpack to change the running result of Webpack.


3. Tapable of Webpack

  • Webpack is essentially an event flow mechanism, and its workflow is to chain plug-ins together, and Tapable is at the heart of it all, The core Compiler in Webpack and Compilation that creates bundles are both instances of Tapable
  • There are various hooks inside Webpack, and plug-ins register their methods with the corresponding hooks, so that when Webpack compiles, these hooks will fire, and therefore the plug-in’s methods

1. Tapable classification

  • Tapable provides many types of hooks, divided into synchronous (Sync) and asynchronous (Async) two categories (asynchronous and distinguish between asynchronous parallel and asynchronous serial port), and depending on the event to perform termination conditions, and derived Basic/Bail/Waterfall/Loop type


type

How to identify

Use the point
Basic

Hook does not contain the following three types of keywords

Does not care if the listener has a return value
Bail

Hook contains Bail

Insurance type: if there is a return value (not undefined) in the listener, the subsequent listener will be skipped
Waterfall

The hook includes Waterfall

Waterfall: The value returned from the previous step is used by the next step
Loop

Hook contains Loop

Loop type: This listener is executed repeatedly if it returns true, and exits the loop if it returns undefined

2. Precautions of all hooks

  • All Hook instantiations take an optional argument, which is a string array of parameter names
  • Parameter names can be filled in arbitrarily, but the length of the parameter array must be the same as the actual number of accepted parameters
  • If the callback function does not accept arguments, it can pass in an empty array
  • The length of the array passed in at instantiation time is useful, but the value is useless
  • Each Hook instance is a publish-and-subscribe event manager that registers events with TAP. The first parameter can be filled in arbitrarily, even if you write comments in Chinese, because the call does not pass the event name and executes all registered events
  • When a call is executed, the number of arguments is related to the length of the array being instantiated

4. Compiler and Compilation

  • Compiler and Compilation inherit from Tapable so that you can subscribe to and emit events.
  • Compiler: When Webpack executes a build, it first reads the Webpack configuration file to instantiate a Compiler object, and then calls its run method to open a complete compilation. The Compiler object represents the complete configuration of the Webpack environment. This object is created once when Webpack is started, and all the actionable Settings are configured, including options, Loader, and plugin. When a plug-in is applied in a Webpack environment, the plug-in receives a reference to this Compiler object. You can use it to access the main Webpack environment.
  • Compilation: Objects represent a build of a version of a resource. When running the Webpack development environment middleware, each time a file change is detected, a new Compilation is created, resulting in a new set of Compilation resources. A Compilation object represents the current module resources, the compile-generated resources, the changing files, and the state information of the dependencies being tracked. The Compilation object also provides a number of critical timing callbacks that plug-ins can choose to use when doing custom processing.

Five, Webpack source code reading skills

1. Find key files

1.1 bin/webpack. Js

node_modules\webpack\bin\webpack.js

  • Open the packge.json file in your project and find webPack, Ctrl + mouse click ==> to quickly find the location of WebPack
// Find the code here
Webpack has two command-line tools: webpack-cli and webpack-command
// Since webpack-cli is more powerful, webpack-cli is usually used, so the following statement is executed
else if (installedClis.length === 1) {
    const path = require("path");
    const pkgPath = require.resolve(`${installedClis[0].package}/package.json`);
    const pkg = require(pkgPath);
    require(path.resolve(
        path.dirname(pkgPath),
        pkg.bin[installedClis[0].binName]
    ));
} Copy the code

1.2 lib/webpack. Js

node_modules\webpack\lib\webpack.js

  • Webpack entry file, you can start from here to read the source

1.3 webpack \ declarations

node_modules\webpack\declarations

  • This directory places declaration files for webPack configuration items/plug-ins written in typescript

1.4 Compiler.js / Compilation.js

node_modules\webpack\lib

  • Enter the following code in the Compiler.js/compiler.js file for Webpack: you can get all the hooks for Webpack

2. The debug code

2.1 Reading Ideas

  • Collapse the logic of the irrelevant branches first, looking only at the body flow code
  • Look for the critical path, guess the intent based on variable names and method names, and verify the idea by reading the source code
  • Debugger critical path to understand the entire execution process

2.2 The first debugging method

  • Copy the code found above in bin/webpack.js to a separate file debugger.js
// Debugger.js is the same as packge.json in the project
// Right clicking debugger.js is equivalent to running webPack with NPX webPack
// NPX webpack uses node to execute cli.js under bin
// npx webpack = node ./node_modules/webpack-cli/bin/cli.js
// Go to webpack-cli/bin/cli.js, set a breakpoint, and start debugging (this is the first method)
const path = require("path");
const pkgPath = require.resolve(`webpack-cli/package.json`);
const pkg = require(pkgPath);
require(path.resolve(
        path.dirname(pkgPath),
        './bin/cli.js'
        //pkg.bin['webpack-cli']
));Copy the code

2.3 The second debugging method

  • Create a new cli.js file, set breakpoints in WebStorm, and right click to run cli.js to start debugging the code
let webpack = require("webpack");
let webpackOptions = require("./webpack.config");
const compiler = webpack(webpackOptions);

compiler.run((err, stat) = > {
  console.log(err);
  console.log(stat)
});Copy the code

Vi. Code analysis after Webpack construction

1. webpack 4

1.1 webpack. Config. Js

const path = require('path');
module.exports = {
    // Package the code in development mode!!!!!!
    mode:'development'.devtool:'none'.entry:'./src/index.js'.output: {path:path.resolve(__dirname,'dist'),
        filename:'bundle.js'}};Copy the code

1.2 the source code

// src/index.js
import {logMsg}  from './sync-module';
console.log(logMsg);
let button = document.createElement('button');
button.innerHTML = 'Please click on me';
button.addEventListener('click', () = > {import(/*webpackChunkName: 'async-module'*/'./async-module.js').then(result= >{
        console.log(result.default);
    });
});
document.body.appendChild(button);


// src/async-module.js
module.exports = "I'm an asynchronous module.";


// src/sync-module.js
export function logMsg() {
    console.log('I'm a sync module');
}Copy the code

1.3 bundle. Js

/* src/ index.js sync-module.js async-module.js */
// When webpack is packaged, the relative path of the reference module is changed to that of webpack.config.js
// add "./sync-module.js" to index.js => will become "./ SRC /sync-module.js"

// Webpack starts the code's self-executing function
(function(modules) { // webpackBootstrap

  // install a JSONP callback for chunk loading
  function webpackJsonpCallback(data) {
    // data => [
      // [chunkName],
      // {chunkID: chunk content}
    // ]
    var chunkIds = data[0];
    var moreModules = data[1];

    // add "moreModules" to the modules object,
    // then flag all "chunkIds" as loaded and fire callback
    var moduleId, chunkId, i = 0, resolves = [];
    for(; i < chunkIds.length; i++) { chunkId = chunkIds[i];if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
        // installedChunks[chunkId] => [resolve, reject, Promise]
        ????????????????
        resolves.push(installedChunks[chunkId][0]);
      }
      // If the value is set to 0, the loading is successful
      installedChunks[chunkId] = 0;
    }
    for(moduleId in moreModules) {
      if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
        // After the asynchronous chunk load is complete, merge the asynchronous chunk code into modules
        // When the chunk is loaded later, it can be retrieved directly from modules
        // Key is the module ID and value is the module contentmodules[moduleId] = moreModules[moduleId]; }}if(parentJsonpFunction){
      parentJsonpFunction(data);
    }

    while(resolves.length) {
      / / resolve executionresolves.shift()(); }}// The module cache
  // Normal module cache: whenever a module is loaded and initialized once, it is put here
  // Get it directly from here, no need to initialize it again
  var installedModules = {};


  // object to store loaded and loading chunks
  // !!!!!!!!!!!!!!!!!!!!!!
  // Store the loaded or loaded chunk (the chunk includes the import chunk and the asynchronously loaded chunk)
  // !!!!!!!!!!!!!!!!!!!!!!
  // installedChunks specifies the value of each key in the installedChunks object
  // undefined = chunk not loaded, null = chunk preloaded/prefetched
  // undefined indicates that chunk is not loaded, and null indicates that chunk is preloaded
  // Promise = chunk loading, 0 = chunk loaded
  // Promise indicates that chunk is loading. 0 indicates that chunk loading is complete
  var installedChunks = {
    // For single entry, the default key is main
    "main": 0
  };


  // script path function
  // Set the request URL for asynchronous chunk
  function jsonpScriptSrc(chunkId) {
    return __webpack_require__.p + "" + chunkId + ".bundle.js"
  }

  // The require function
  Webpack implements a require method that can be run directly in a browser
  function __webpack_require__(moduleId) {

    // Check if module is in cache
    // Before loading a module, check the cache list to see if it has been loaded
    if(installedModules[moduleId]) {
      // If yes, the module has been cached and returns the exported object of the module
      return installedModules[moduleId].exports;
    }
    // Create a new module (and put it into the cache)
    // Create a new module object and place it in the cache list
    var module = installedModules[moduleId] = {
      / / module ID
      i: moduleId,
      // Loaded :false
      l: false.// The module exports the object, which is an empty object by default
      exports: {}
    };

    // Execute the module function
    // Load the module
    Modules is an argument received by a self-executing function -- an object containing module information
    // Key is the module path, value is a function that contains the contents of the module
    / / {
    // "./src/a.js":
    // (function(module, __webpack_exports__, __webpack_require__) {
    // Module content: XXX
    // Module content: XXX
    // Module content: XXX
    / /},
    // }
    // Find the corresponding key from modules, execute the function (value) and point the internal this to the exports object of the new Module above
    // The purpose is to place the contents inside functions in module.exports
    modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);

    // Flag the module as loaded
    // Set it to loaded
    module.l = true;

    // Return the exports of the module
    // Finally returns the contents of the current module
    return module.exports;
  }

  // This file contains only the entry chunk.
  // The chunk loading function for additional chunks
  // Load asynchronous chunk
  __webpack_require__.e = function requireEnsure(chunkId) {
    var promises = [];

    // JSONP chunk loading for javascript
    // Use JSONP to request loading of asynchronous chunks
    var installedChunkData = installedChunks[chunkId];

    // 0 means "already installed".
    // If the chunk to load is not initialized
    if(installedChunkData ! = =0) {

      // a Promise means "currently loading".
      // If 0 is excluded, undefined/null/Promise is left in the cache chunk list
      // When the module is being loaded
      if(installedChunkData) {
        promises.push(installedChunkData[2]);
      }
      // When the module has not been loaded yet
      else {
        // setup Promise in chunk cache
        var promise = new Promise(function(resolve, reject) {
          // When a new promise is created, its function body is immediately executed
          // Set the state of the current chunk to Promise, indicating that it is loading
          installedChunkData = installedChunks[chunkId] = [resolve, reject];
        });
        // Add a value to the current installedChunkData
        // Then add installedChunkData to Promises array
        promises.push(installedChunkData[2] = promise);

        // start chunk loading
        // Use JSONP to request loading of asynchronous chunks
        var script = document.createElement('script');
        var onScriptComplete;

        script.charset = 'utf-8';
        script.timeout = 120;
        if (__webpack_require__.nc) {
          script.setAttribute("nonce", __webpack_require__.nc);
        }
        // Set the request URL
        script.src = jsonpScriptSrc(chunkId);

        // create error before stack unwound to get useful stacktrace later
        var error = new Error(a); onScriptComplete =function (event) {
          // avoid mem leaks in IE.
          script.onerror = script.onload = null;
          clearTimeout(timeout);
          var chunk = installedChunks[chunkId];
          if(chunk ! = =0) {
            if(chunk) {
              var errorType = event && (event.type === 'load' ? 'missing' : event.type);
              var realSrc = event && event.target && event.target.src;
              error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ':' + realSrc + ') ';
              error.name = 'ChunkLoadError';
              error.type = errorType;
              error.request = realSrc;
              chunk[1](error);
            }
            installedChunks[chunkId] = undefined; }};var timeout = setTimeout(function(){
          onScriptComplete({ type: 'timeout'.target: script });
        }, 120000);
        script.onerror = script.onload = onScriptComplete;
        document.head.appendChild(script); }}// Execute all promises and return the result
    return Promise.all(promises);
  };

  // expose the modules object (__webpack_modules__)
  // Place the list of modules on the m attribute __webpack_require__
  __webpack_require__.m = modules;

  // expose the module cache
  // Place the cache list on the __webpack_require__ c property
  __webpack_require__.c = installedModules;

  // define getter function for harmony exports
  // Define the getter for the name property on the exports object
  __webpack_require__.d = function(exports, name, getter) {
    // Check whether the name attribute exists on the exports object
    if(! __webpack_require__.o(exports, name)) {// Add the name attribute to the exports object, which can be enumerated to true
      // The value of get is getter, which is executed when the property is accessed
      Object.defineProperty(exports, name, { enumerable: true.get: getter }); }};// define __esModule on exports
  // Define an __esModule attribute on the Exports object that determines whether the current module is an ES6 module
  __webpack_require__.r = function(exports) {
    // If the current browser supports Symbol
    if(typeof Symbol! = ='undefined' && Symbol.toStringTag) {
      / / set before
      // console.log(exports.toString()); // [object Object]
      // Set the exports object type to Module
      Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
      / / set
      // console.log(exports.toString()); // [object Module]
    }
    // Add an ESM attribute to the Exports object otherwise
    Object.defineProperty(exports, '__esModule', { value: true });
  };

  // Create a fake Namespace object Creates a namespace object/ Why create a namespace object?// Because import('xxx.js') loads js, it can be ESM or CJS
  // So compatibility processing is required
  // mode & 1: value is a module id, require it
  // mode & 2: merge all properties of value into the ns nameSpace
  // mode & 4: return value when already ns object
  / / mode & 8 | 1: fitting concepts like the require the same as the require
  // how to use binary mode? Efficient. To save memory
  7 => 111 readable, writable and executable
  __webpack_require__.t = function(value, mode) {
    // value starts with the module ID
    // Load the module directly
    if(mode & 1) value = __webpack_require__(value);
    // Return the contents of the module without loading the module
    if(mode & 8) return value;
    // Return value if value is already an object and the __esModule attribute is true
    if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    // Otherwise create an empty object, load it,
    var ns = Object.create(null);
    // Set the __esModule property to true on the object
    __webpack_require__.r(ns);
    // Define a default attribute for the ns object
    Object.defineProperty(ns, 'default', { enumerable: true.value: value });
    // If mode is 2 and value is not a string, define all attributes of the value to the ns object
    if(mode & 2 && typeofvalue ! ='string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
    return ns;//{__esModule:true,default:' module contents '}
  };

  // getDefaultExport function for compatibility with non-harmony modules
  // a function that gets the contents of the module
  __webpack_require__.n = function(module) {
    // If __esModule is an ES6 module, return the default attribute of the module
    // If not, it is a CJS module and returns the module itself
    var getter = module && module.__esModule ?
      function getDefault() { return module['default']; } :
      function getModuleExports() { return module; };
    // Add a property of A to the getter, the getter method itself
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };

  // Object.prototype.hasOwnProperty.call
  __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

  // __webpack_public_path__
  // Expose the access path
  __webpack_require__.p = "";

  // on error function for async loading
  // Error output while loading asynchronous chunk
  __webpack_require__.oe = function(err) { console.error(err); throw err; };


  // The window["webpackJsonp"] will be an empty array when first executed
  // jsonpArray and window["webpackJsonp"] both point to the same memory address
  var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] | | [];// Bind this to keep the old array push method pointing to jsonpArray
  var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
  ParentJsonpFunction (data) is executed in webpackJsonpCallback if this is not bound
  // This is equivalent to a "naked" array native push, where data doesn't know who to add to
  // var oldJsonpFunction = jsonpArray.push;

  // Override the jsonArray push method and assign it to webpackJsonpCallback
  jsonpArray.push = webpackJsonpCallback;
  jsonpArray = jsonpArray.slice();
  for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
  // Why keep the old array push method?
  // Avoid "repeated requests" to load chunks, if they are already loaded, use them directly
  var parentJsonpFunction = oldJsonpFunction;

  / / summary:
  // the push method of window["webpackJsonp"] has been overridden to implement the jsonp callback instead of the array native method
  // 2, If you want to add data to the array window["webpackJsonp"], you can't push it
  // 3, so define a jsonpArray array, which points to the same memory address as window["webpackJsonp"]
  // 4. Add (push) data to jsonpArray, and the corresponding window["webpackJsonp"] gets the data

  // Load entry module and return exports
  // Load the entry module and return the exported object
  return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* src/ index.js sync-module.js async-module.js */
// When webpack is packaged, the relative path of the reference module is changed to that of webpack.config.js
// add "./sync-module.js" to index.js => will become "./ SRC /sync-module.js"

({
  // Key is the module ID and value is the module content
  "./src/index.js":
  / *! * * * * * * * * * * * * * * * * * * * * * *! *\ // entry chunk! *** ./src/index.js ***! \ * * * * * * * * * * * * * * * * * * * * * * /
  / *! no exports provided */
    (function(module, __webpack_exports__, __webpack_require__) {

      "use strict";
      __webpack_require__.r(__webpack_exports__);
      /* harmony import */ var _sync_module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./sync-module */ "./src/sync-module.js");

      console.log(_sync_module__WEBPACK_IMPORTED_MODULE_0__["logMsg"]);

      let button = document.createElement('button');
      button.innerHTML = 'Please click on me';
      button.addEventListener('click',()=>{
        __webpack_require__.e(/ *! import() | async-module */ "async-module")
          .then(__webpack_require__.t.bind(null./ *! ./async-module.js */ "./src/async-module.js".7))
          .then(result= >{// result = {__esModule:true,default:' module contents '}
          console.log(result.default);
        });
      });
      document.body.appendChild(button);
    }),

  "./src/sync-module.js":
  / *! * * * * * * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/sync-module.js ***! / / entrance to the chunk rely on synchronous module \ * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
  / *! exports provided: logMsg */
    (function(module, __webpack_exports__, __webpack_require__) {

      "use strict";
      __webpack_require__.r(__webpack_exports__);
      /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "logMsg".function() { return logMsg; });

      function logMsg() {
        console.log('I'm a sync module'); }})});Copy the code

2. webpack 5

  • The code is slightly easier to read than WebPack 4
(function(modules) { // The webpack startup code executes itself
  // The module cache Is The cache of The module
  var installedModules = {};

  // The require function webpack implements its own require method
  function __webpack_require__(moduleId) {

    // Check if module is in cache
    if(installedModules[moduleId]) {
      return installedModules[moduleId].exports;// If yes, this module is loaded and returns exported object exports directly
    }
    // Create a new module (and put it into the cache)
    // Create a new module object and place it in the cache
    var module = installedModules[moduleId] = {
      i: moduleId,/ / module ID
      l: false.// Whether loaded false
      exports: {} // Export object, default is an empty object
    };

    // Execute the module function executes the corresponding method of this module to assign a value to module.exports
    modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);

    // Flag the module as loaded
    module.l = true;

    // Return the exports of the module returns the exports of the module
    return module.exports;
  }

  // the startup function
  function startup() {
    // Load entry module and return exports
    // Load the entry module and return the exported object
    return __webpack_require__("./src/index.js");
  }

  // run startup Executes the startup method
  returnstartup(); ({})"./src/hello.js":
 (function(module) {
   module.exports = "hello";
 }),
 "./src/index.js":
 (function(__unusedmodule, __unusedexports, __webpack_require__) {
    let hello = __webpack_require__( "./src/hello.js");
    console.log(hello); })});Copy the code

Abstract Syntax Tree

1. What is AST

  • JavaScript Parser transforms the code into an abstract syntax tree (AST), which defines the structure of the code. By manipulating this tree, we can accurately locate declaration statements, assignment statements, operation statements, etc., so as to realize the code analysis, optimization, and change operations.


2. The AST purposes

  • Code syntax checks, code style checks, code formatting, code highlighting, code error prompts, code auto-completion, and so on
  • JSLint, JSHint checks for code errors or styles to find potential errors
  • IDE error prompts, formatting, highlighting, autocomplete, and more
  • Code obfuscation compression
  • UglifyJS2 etc.
  • Optimization changes the code, changes the code structure to achieve the desired structure
  • Code packaging tools Webpack, Rollup, and more
  • CommonJS, AMD, CMD, UMD, etc
  • CoffeeScript, TypeScript, JSX, etc. Convert to native Javascript

3. AST Execution process

  • Parsing source code
  • Lexical Analysis: The Tokenizer converts code strings into arrays of syntax units — Tokens — at this stage. For (const item of items) {}

The syntactic units in Javascript code include the following

  • Key words:const,let,varEtc.
  • Identifiers: Can be a variable, keywords such as if or else, or constants such as true or false
  • The operator
  • digital
  • The blank space
  • annotation
  • Syntactic Analysis: At this stage the Parser converts Tokens into abstract syntax trees
  • Depth-first traverses the syntax tree, modifying the syntax tree
  • Convert the syntax tree back to source code



4. JavaScript Parser

  • JavaScript Parser is a Parser that converts JavaScript source code into abstract syntax trees.
  • The browser converts the JS source code through the parser into an abstract syntax tree, which is further converted into bytecode or directly generated into machine code.
  • Generally speaking, each JS engine has its own abstract syntax tree format. Chrome V8 engine, Firefox SpiderMonkey engine, etc. MDN provides a detailed description of the SpiderMonkey AST format, which is the industry standard.

5. Tools needed in the project

  • astexplorer
  • @babel/core has Babylon/Parser built in, which can also be used to convert AST
  • @babel/parser is a JavaScript parser used in Babel.
  • @babel/traverse maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
  • @babel/types contains methods for building ASTs manually and for checking the types of AST nodes.
  • @babel/generator Turns an AST into code.

6. AST Usage examples

6.1 Converting arrow functions

const babylon = require('@babel/parser');
// @babel/core has Babylon /parser built in, which can also be used to convert AST
const babel = require('@babel/core');
let types = require('@babel/types');
let generate = require('@babel/generator').default;
let traverse = require('@babel/traverse').default;

const originalSource = "const a = (a, b) => a + b;";

// Convert the contents of the current module into an AST
const ast = babylon.parse(originalSource);
// @babel/core has Babylon /parser built in, which can also be used to convert AST
// const ast = babel.parse(originalSource);

// Walk through the syntax tree to find the target node to modify
traverse(ast, {
    // If the current node is an arrow function
    ArrowFunctionExpression: (nodePath) = > {
        let node = nodePath.node;
        let body = node.body;
        if(! types.isBlockStatement(node.body)){ body = types.blockStatement([types.returnStatement(node.body)]) }let newNode = types.functionExpression(null,node.params,body); nodePath.replaceWith(newNode); }});// Re-generate the code from the transformed abstract syntax tree
let {code} = generate(ast);
console.log('New code =>', code);Copy the code

6.2 conversion class

const babylon = require('@babel/parser');
let types = require('@babel/types');
let generate = require('@babel/generator').default;
let traverse = require('@babel/traverse').default;
const originalSource = `class Person{ constructor(name){ this.name = name; } getName(){ return this.name } }`;
// Convert the contents of the current module into an AST
const ast = babylon.parse(originalSource);
// Walk through the syntax tree to find the target node to modify
traverse(ast, {
    // If the current node is a class
    ClassDeclaration: (nodePath) = > {
        let node = nodePath.node;
        let bodys = node.body.body;
        let id = node.id;
        bodys = bodys.map(body= > {
            if (body.kind === 'constructor') {
                return types.functionExpression(id, body.params, body.body)
            } else {
                let left = types.memberExpression(id, types.identifier('prototype'));
                left = types.memberExpression(left, body.key);
                let right = types.functionExpression(null, body.params, body.body);
                return types.assignmentExpression('=', left, right); }}); nodePath.replaceWithMultiple(bodys); }});// Re-generate the code from the transformed abstract syntax tree
let {code} = generate(ast);
console.log('New code =>', code);Copy the code

Eight, after the language

  • This article involves some knowledge points, speak of shallow, interested can consult relevant information in-depth understanding.
  • Because the article was written on the sparrow before, so some pictures of the watermark became mine, if offended please forgive me, please tell me the original address, I will add in the back, thank you.

9. Refer to the article

Github.com/jamiebuilds…

Developer.mozilla.org/zh-CN/docs/…

Juejin. Cn/post / 684490…

Github.com/webpack/tap…

www.jianshu.com/p/273e1c990…

Ten, the source code

Github.com/yjdjiayou/e…