Webpack is one of the mainstream front-end packaging tools at present, but for the students who just started, WebPack is actually a bit complicated. Probably most people (including me) will only build projects using one-click scaffolding like Creact-React-app. So what are the basic concepts behind WebPack? So today to write a basic version of Webpack in JS.

Webpack is simple

  • Package the JS in a bunch of JS files, merge them into one or several files that the browser can recognize, and then put them into HTML to display in the browser. The most direct manifestation is to package ES6 JS code into ES5 code and let the browser compile it.

Package using ES5 syntax

  1. The project structure

  • index.js
let first = require('./first.js').first;
let second = require('./second.js').second;

let message = `${name} is ${action}`;
console.log( message );
Copy the code
  • first.js
let first = 'Hello';

exports.first = first;
Copy the code
  • second.js
let third = require('./third').third;

exports.second = `Let's ${third}`;
Copy the code
  • third.js
exports.third = 'making webpack';
Copy the code

At this time to runnode index.js, the printed result is:

So, this is the most basic implementation of WebPack, packaging all the modular code into a single file and running it. Note, however, that the browser does not know the exports, require code, which can only be used in the Node environment. Therefore, we need to manually set the require function and exports object

  1. Set all file code as functions
//index.js
function(require, exports) {
    let first = require('./first.js').first;
    let second = require('./second.js').second;

    let message = `${first}, ${second}`;
    console.log( message );
}

//first.js
function(require, exports) {
    let first = 'Hello';
    exports.first = first;
}

//second.js
function(require, exports) {
    let third = require('./third.js').third;
    exports.second = `Let's ${third}`;
}

//third.js
function(require, exports) {
    exports.third = 'making webpack';
}
Copy the code
  1. Desired goals

This code is processed by running a function that eventually generates a Module object

modules = { //fn 0: [function(require, exports) { let first = require('./first.js').first; let second = require('./second.js').second; let message = `${first},  ${second}`; console.log( message ); }, //mapping { './first.js': 1, './second.js': 2 } ], //fn 1: [function(require, exports) { let first = 'Hello'; exports.first = first; }, //mapping {} ], //fn 2: [function(require, exports) { let third = require('./third').third; exports.second = `Let's ${third}`; }, //mapping { './third.js': 3 } ], //fn 3: [function(require, exports) { exports.third = 'making webpack'; }, //mapping {} ] }Copy the code

It’s called by a function

function package(id) {
  let [fn, mapping] = modules[id];
  let exports =  {};
  
  function require(path) {
    return exec(mapping[path]);
  }
  
  fn && fn(require, exports);
  return exports;
}

package(0)
Copy the code

Finally, write it to the bundle.js file in the dist directory

  1. How to generate module objects?
  • The first thing you need to do is look at what files are required in a single file
function getDependencies(fileContent) { let reg = /require\(['"](.+?) ['"]\)/g; let result = null; let dependencies = []; while(result = reg.exec(fileContent)) { dependencies.push(result[1]); } console.log('dependencies', dependencies) return dependencies; } // example let fileContent = fs.readfilesync ('./index.js', 'utF-8 '); getDependencies(fileContent)Copy the code

If it matches the rule, add it to the Dependencies array.

dependencies [ './first.js', './second.js' ]
Copy the code
  • Next, you need a function that generates an include based on filenameid,filename,dependencies,codeIs an object of
let ID = 0; function createObject(filename) { let fileContent = fs.readFileSync(filename, 'utf-8'); let id = ID++; const item = { id: id, filename: filename, dependencies: getDependencies(fileContent), code: `function(require, exports, module) { ${fileContent} }` } console.log('item', Return item} // Example createObject(./index.js)Copy the code

Results:

item { id: 0, filename: './src/index.js', dependencies: [ './first.js', './second.js' ], code: 'function(require, exports, module) { \n' + " let first = require('./first.js').first; \r\n" + "let second = require('./second.js').second; \r\n" + '\r\n' + 'let message = `${first}, ${second}`; \r\n' + 'console.log( message ); \r\n' + '\n' + ' }' }Copy the code
  1. According to thedependenciesindex.jsAll of therequireThe file carries the samecreateObjectoperation
function createQueue(filename) { let item = createObject(filename); let queue = [item]; for(let item of queue) { const dirname = path.dirname(item.filename); item.mapping = {}; Item. The dependencies. The forEach (relativePath = > {/ / get absolute path to the file const absolutePath = path. Join (dirname, relativePath); Const Child = createObject(absolutePath); item.mapping[relativePath] = child.id; queue.push(child); }); } console.log('queue', queue) return queue; } // queue [{id: 0, filename: './ SRC /index.js', dependencies: ['./first.js', './second. 'function(require, exports, module) { \n' + " let first = require('./first.js').first;\r\n" + "let second = require('./second.js').second;\r\n" + '\r\n' + 'let message = `${first}, ${second}`;\r\n' + 'console.log( message );\r\n' + '\n' + ' }', mapping: { './first.js': 1, './second.js': 2 } }, { id: 1, filename: 'src\\first.js', dependencies: [], code: 'function(require, exports, module) { \n' + " let first = 'Hello';\r\n" + '\r\n' + 'exports.first = first;\r\n' + '\n' + ' }', mapping: {} }, { id: 2, filename: 'src\\second.js', dependencies: [ './third.js' ], code: 'function(require, exports, module) { \n' + " let third = require('./third.js').third;\r\n" + '\r\n' + "exports.second = `Let's ${third}`;\r\n" + '\n' + ' }', mapping: { './third.js': 3 } }, { id: 3, filename: 'src\\third.js', dependencies: [], code: 'function(require, exports, module) { \n' + " exports.third = 'making webpack';\r\n" + '\r\n' + '\n' + ' }', mapping: {}}]Copy the code
  1. Generates a string of module objects as content from queue and writes it to bundle.js
function createBundle(graph) { let modules = '' graph.forEach(mod => { modules += `${mod.id}: [ ${mod.code}, ${JSON.stringify(mod.mapping)} ],` }) const result = ` const modules = {${modules}} function fetch(data){ function exec(id) { let [fn, mapping] = modules[id]; let module = { exports: {} }; fn && fn(require, module.exports, module); function require(path) { return exec(mapping[path]); } return module.exports; } exec(0) } fetch(modules) ` fs.writeFileSync('.. /dist/bundle.js', result) }Copy the code
  1. All the code
const fs = require('fs') const path = require('path') let ID = 0 let fileContent = fs.readFileSync('./src/index.js', 'utf-8') function getDependencies(str) { let reg = /require\(['"](.+?) ['"]\)/g let result = null let dependencies = [] while (result = reg.exec(str)) { dependencies.push(result[1]) } return dependencies } function createObject(filename) { console.log(filename) let fileContent = fs.readFileSync(filename, 'utf-8') console.log(11111111111) const id = ID++ const item = { id: id, filename: filename, dependencies: getDependencies(fileContent), code: `function(require, exports, module) { ${fileContent} }` } return item } function createQueue(filename) { let item = createObject(filename) let queue  = [item] console.log(queue) for (let item of queue) { const dirname = path.dirname(item.filename) item.mapping = {} Item. The dependencies. The forEach (relativePath = > {/ / get the absolute path to the console file. The log (1) the const absolutePath = path. Join (dirname, RelativePath) console.log(2) Call createObject const Child = createObject(absolutePath) console.log(3) item.mapping[relativePath] = child.id console.log(4) queue.push(child) }) } return queue } function createBundle(graph) { let modules = '' graph.forEach(mod => { modules += `${mod.id}: [ ${mod.code}, ${JSON.stringify(mod.mapping)} ],` }) const result = ` const modules = {${modules}} function fetch(data){ function exec(id) { let [fn, mapping] = modules[id]; let module = { exports: {} }; fn && fn(require, module.exports, module); function require(path) { return exec(mapping[path]); } return module.exports; } exec(0) } fetch(modules) ` fs.writeFileSync('.. /dist/bundle.js', result) }// let ccc = fs.readFileSync('/src/third.js', 'utf-8'); // console.log('ccc', ccc) let queue = createQueue('./src/index.js') createBundle(queue)Copy the code
  1. Result: generated code in dist/bundle.js
const modules = {0: [ function(require, exports, module) { let first = require('./first.js').first; let second = require('./second.js').second; let message = `${first}, ${second}`; console.log( message ); }, {"./first.js":1,"./second.js":2} ],1: [ function(require, exports, module) { let first = 'Hello'; exports.first = first; }, {} ],2: [ function(require, exports, module) { let third = require('./third.js').third; exports.second = `Let's ${third}`; }, {"./third.js":3} ],3: [ function(require, exports, module) { exports.third = 'making webpack'; }, {} ],} function fetch(data){ function exec(id) { let [fn, mapping] = modules[id]; let module = { exports: {} }; fn && fn(require, module.exports, module); function require(path) { return exec(mapping[path]); } return module.exports; } exec(0)} fetch(modules) // Run Hello, Let's make webpackCopy the code

So, this is what the most simplified version of WebPack does