As a front-end development engineer, presumably everyone in the work and study will not be able to deal with Webpack, this article from the perspective of handwritten Webpack, step by step with you to achieve a simple version of Webpack, by the way let you understand the principle of Webpack. The source address

Basic implementation process

  • Reads the contents of the entry file
  • Analyze the entry file, read the module and recursively read the file content that the module depends on, and generate the AST syntax tree.
  • Generate code that the browser can run from the AST syntax tree

Tool Module Package

  • path: Path operation
  • fs: Read and write files
  • @babel/parser: Converts content into an AST syntax tree
  • @babel/traverse: Traverses the AST collection dependencies
  • @ Babel/core and @ Babel/preset - env: Converts ES6 syntax to ES5

Function implementation

Module analysis: read entry files and analyze code

const fs = require("fs");
const main = filename= > {
 const content = fs.readFileSync(filename, "utf-8");
 console.log(content);
};
main("./index.js");
Copy the code

Turn the content into an AST syntax tree

Using @babel/ Parser, which is babel7’s tool, to help us analyze the internal syntax, including ES6, returns an AST abstract syntax tree. We parse not only the contents of the index.js file, but also other information about the file. And its contents are actually in the body of its property program.

  • Install @ Babel/parser
$ npm install @babel/parser --save
Copy the code
  • Specific code
const fs = require("fs");
const path = require("path");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const parse = filename= > {
    const content = fs.readFileSync(filename, "utf-8");
    const Ast = parser.parse(content, {
        sourceType: "module"
    });
    console.log(Ast.program.body);
};
parse("./index.js");
Copy the code

Traverse the AST collection dependencies

We can then iterate through all the imported modules based on the analysis results in the body. This is a collection of file paths that were imported with the import statement. Babel’s module @Babel/Traverse is recommended here to help us with this.

  • The installation@babel/traverse
$ npm install @babel/traverse --save
Copy the code
  • Specific code
const parse = filename= > {
    const content = fs.readFileSync(filename, "utf-8");
    const Ast = parser.parse(content, {
        sourceType: "module"
    });
    const dependencies = [];
    // Parse the AST abstract syntax tree and return the corresponding data as needed.
    // Return the corresponding module based on the result, define an array, and accept the value of node.source.value
    traverse(Ast, {
        ImportDeclaration({ node }) {
            console.log(node); dependencies.push(node.source.value); }});console.log(dependencies);
};
parse("./index.js");
Copy the code

ES6 to ES5 (AST)

Now we need to convert the AST obtained for ES6 to ES5. For this step, we need @babel/core and @babel/preset-env.

  • The installation@ Babel/core and @ Babel/preset - env
npm install @babel/core @babel/preset-env
Copy the code
  • Specific code
const parse = filename= > {
    const content = fs.readFileSync(filename, "utf-8");
    const Ast = parser.parse(content, {
        sourceType: "module"
    });
    const dependencies = [];
    // Parse the AST abstract syntax tree and return the corresponding data as needed.
    // Return the corresponding module based on the result, define an array, and accept the value of node.source.value
    traverse(Ast, {
        ImportDeclaration({ node }) {
            console.log(node); dependencies.push(node.source.value); }});const {code} = babel.transformFromAst(ast,null, {presets: ["@babel/preset-env"]})console.log(code);
};
parse("./index.js");
Copy the code

Get all dependencies recursively

Return an object from the parse method above, create a new run function, and write a recursive method inside it to recursively retrieve the dependencies.

const run = filename= > {
  const entry =  parse(filename)
  const temp = [entry]
  / /! Handle other dependent modules and make a summary
  for (let i = 0; i < temp.length; i++) {
    const item = temp[i];
    const { dependencies } = item;
    if (dependencies) {
      for (let j independencies) { temp.push(parse(dependencies[j])); }}}console.log(temp)
}
const parse = filename= > {
    const content = fs.readFileSync(filename, "utf-8");
    const Ast = parser.parse(content, {
        sourceType: "module"
    });
    const dependencies = [];
    // Parse the AST abstract syntax tree and return the corresponding data as needed.
    // Return the corresponding module based on the result, define an array, and accept the value of node.source.value
    traverse(Ast, {
        ImportDeclaration({ node }) {
            console.log(node); dependencies.push(node.source.value); }});const {code} = babel.transformFromAst(ast,null, {presets: ["@babel/preset-env"]})return {
      entryFile,
      dependencies,
      code
    };
};
run("./index.js");
Copy the code

The customrequireandexports

Browsers do not recognize require and exports. We can define require and exports ourselves.

file(code) {
  / /! Generate bundle.js =>./dist/main.js
  const filePath = path.join(this.output.path, this.output.filename);
  console.log(filePath);
  const newCode = JSON.stringify(code);
  const bundle = `(function(graph){
      function require(module){
          function localRequire(relativePath){

             return require( graph[module].dependencies[relativePath])
          }
          var exports={};
          (function(require,exports,code){
              eval(code)
          })(localRequire,exports,graph[module].code)

          return exports;
      }
      require('The ${this.entry}') //./src/index
  })(${newCode}) `;
  fs.writeFileSync(filePath, bundle, "utf-8");
}
Copy the code

The complete code

The complete code for these functions can be found at webpack.js

A functional test

  • insrcThere are three new filesexpo.js,index.js,test.js
///test.js
export const test = function() {
  console.log("test webpack");
};

//expo.js
import { test } from "./test.js";
test()
export const add = function(a, b) {
  return a + b;
};

export const minus = function(a, b) {
  return a - b;
};

//index.js
import { add,minus} from "./expo.js";
console.log('add (1, 2)',add(1.2));
console.log(' 'minus (10, 3),minus(10.3));
Copy the code
  • Run the command to generate the following code:
(function(graph){
        function require(module){
            function localRequire(relativePath){
               
               return require( graph[module].dependencies[relativePath])
            }
            var exports= {}; (function(require.exports,code){
                eval(code)
            })(localRequire,exports,graph[module].code)
            
            return exports;
        }
        require('./src/index.js') //./src/index({})"./src/index.js": {"dependencies": {"./expo.js":"./src\\expo.js"},"code":"\"use strict\"; \n\nvar _expo = require(\"./expo.js\"); . \ n \ nconsole log (add (1, 2), (0, _expo. Add) (1, 2)); \ nconsole log (' minus' (10, 3), (0, _expo. Minus) (10, 3));"},"./src\\expo.js": {"dependencies": {"./test.js":"./src\\test.js"},"code":"\"use strict\"; \n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n}); \nexports.minus = exports.add = void 0; \n\nvar _test = require(\"./test.js\"); \n\n(0, _test.test)(); \n\nvar add = function add(a, b) {\n return a + b; \n}; \n\nexports.add = add; \n\nvar minus = function minus(a, b) {\n return a - b; \n}; \n\nexports.minus = minus;"},"./src\\test.js": {"dependencies": {},"code":"\"use strict\"; \n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n}); \nexports.test = void 0; \n\nvar test = function test() {\n console.log(\"test webpack\"); \n}; \n\nexports.test = test;"}})
Copy the code
  • Run the above code in the Chrome console and enter the following: Test successful!
VM2516:9 test webpack
VM2514:5 add(1.2) 3
VM2514:6 minus(10.3) 7
Copy the code

More and more

The source address

Summary of Web development knowledge

Refer to the article

Write the core principles of Webpack by hand, and no longer fear interviewers asking me about the principles of Webpack