The basic process of packaging

  1. Gets the contents of the entry file.
  2. Starting from the entry file, the AST abstract syntax tree is generated by recursively analyzing the files that the template depends on.
  3. Traverse the AST abstract syntax tree to collect dependencies and produce arrays of dependencies.
  4. Convert ES6 code in the AST abstract syntax tree to ES5.
  5. Generate code that normal browsers can recognize.

Plug-in download

Babel traverse @babe/ Core,@babel/ Parser,@babel/traverse

The preparatory work

The directory structure

Entry file main.js

info.js

info2.js

Start writing

Create a fc-webpack.js file in the directory

1. Obtain the contents of the import file

const fs = require('fs'); function createAssets(filename){ const content = fs.readFileSync(filename, 'utf-8'); Console. log(content); //1. }Copy the code

Take a look at the contents of the entry file

2. Generate the AST abstract syntax tree

const fs = require('fs'); const parse = require('@babel/parser'); function createAssets(filename){ const content = fs.readFileSync(filename, 'utf-8'); Parse (content, {//2) const ast = parse.parse(content, {//2) Get abstract syntax tree sourceType: 'module'}); console.log(ast); }Copy the code

Take a look at the abstract syntax tree

4. Facilitate AST abstract syntax tree and collect dependencies

const fs = require('fs'); const parse = require('@babel/parser'); const traverse = require('@babel/traverse').default; function createAssets(filename){ const content = fs.readFileSync(filename, 'utf-8'); Parse (content, {//2) const ast = parse.parse(content, {//2) Get abstract syntax tree sourceType: 'module'}); const dependices = []; // Traverse (ast, {ImportDeclaration: (path) => {// Add a dependices.push(path.node.source.value) to the traverse(ast, {ImportDeclaration: (path) => {// Add a dependices.push(path.node.source.value); } }) console.log(dependices) }Copy the code

Take a look at the dependencies collected

5. Convert ES6 to ES5

const fs = require('fs'); const parse = require('@babel/parser'); function createAssets(filename){ const content = fs.readFileSync(filename, 'utf-8'); Parse (content, {//2) const ast = parse.parse(content, {//2) Get abstract syntax tree sourceType: 'module'}); Const code = Babel. TransformFromAstSync (ast, null, {/ / es6 code into presets es5 code: ['@babel/preset-env'], }) console.log(code); }Copy the code

Take a look at the transformed code

Finally, we’ll have a file name, the code in the file, the file name that the file depends on, and an incremented ID

let Id=0;
function createAssets(filename){
    ...........
    return {filename, dependices, code: code.code, id: ID++}
}
Copy the code

6. Recurse all modules to generate module queues

function createGraph(entry) { const mainAssets = createAsset(entry); Const queue = [mainAssets]; For (let assets of queue) {// Let dirname = path.dirname(assets. Filename); Assets ['maping'] = {}; Assets. Dependices. ForEach (item = > {/ / facilities of the current module depends on the item for module file name let absolutePath = path. Join (dirname, item); Const Child = createAsset(absolutePath); assets.maping[item] = child.id; Queue.push (child); // Generate a mapping of relative paths and ids for each module. }) } return queue; } let graph = createGraph('./main.js'); console.log(graph)Copy the code

Take a look at the queue that produces all the modules

7. Complete the bundle function to generate executable code

The bundle function is basically a convenience to consolidate the list of all the modules of the graph we generated earlier into a self-executing JS file.

function bundle(graph) { let modules = `` graph.forEach(item => { modules += ` ${item.id}:[ function {// require,exports method ${item.code}}, ${json.stringify (item.maping)}], ` }) const result = ` (function(modules){ })({${modules}}) ` return result; // Here is the output of the file, which is a self-executing function}Copy the code

This iterates through the graph passed in to generate an object in json form. Key is the id of the current module,value is an array, the first item of the array is an executable function that calls the code under the current module, and the second parameter of the array is the dependent object of the current module (in the previous section, mapping objects with relative paths and ids for each module object).

8. Handle the keywords require and exports.

It is not possible to execute the generated request code because the browser does not recognize require and exports.

Can’t recognize why? The require function and exports object are not defined. Then we can define it ourselves.

function bundle(graph) { let modules = `` graph.forEach(item => { modules += ` ${item.id}:[ function {// require,exports method ${item.code}}, ${json.stringify (item.maping)}], }) const result = '(function(modules){function require(id){// Const [fn,maping]=modules[id]; Function localRequire(relativePath){function localRequire(relativePath){ Return require(maping[relativePath]); maping[relativePath]; } let exports={}; fn(localRequire,exports); // exports return export and require // exports return export; } require(0); })({${modules}}) 'return result; }Copy the code