Simple version implementation of commonjs module

Keywords: node.js JSON

Implementing Simple Version of commonjs Loading

Read the code with the following questions.

  • Is the file in node module?
  • How is the js file read executed?
  • exports module.exports relationship?
  • Reference relationships between objects in js?
How do you understand that a file is a module?

In node, JS code is wrapped into the following function when it is introduced. Does it mean that we only need to call this function in our require function (of course, the following function needs to read the file content by ourselves, then assemble it into the following function form), parse the function string as executable js, and pass in the reference parameters? module.exports will be assigned. Return to module.exports and you get the content of the module you need.


// (function(exports,module,require){
    this == module.exports // true
    module.exports = 'hello';
// })
module.exports and exports
exports = module.exports

exports.a = 'xxx' // A reference address, module.exports, is also assigned
exports = 'xxx' // At this point, the quotation address of exports is wrong. You can't get the content of the module.
Reference to parameters

But the parameter we pass to the function is a reference to type data. When a function is executed, the parameter assignment stage is executed first. If the type data is referenced, the assignment is a reference address. The following will happen

var test = {}

function fn(a){
    a.name = 'xxx'
}

fn(test)

console.log(test) // {name:'xxx'}

The code is implemented as follows
const fs = require("fs");
const path = require("path");
const vm = require("vm");

function Module(filePath) {
  this.id = filePath;
  this.exports = {};
}

Module._cache = {};

Module.fnStr = ["(function(module,exports,req,__fileName,__dirname){\n", "})"];

Module.extensions = {
  ".js": function(module) {
    const content = fs.readFileSync(module.id, "utf8");
    const fnString = Module.fnStr[0] + content + Module.fnStr[1];
    const fn = vm.runInThisContext(fnString);
    // The file is the module exports = module. export exports, which is actually the module to which you point.
    // The module.exports passed in are assigned after the function is executed. Module is another object, and the reference of function parameters assigns values to the module instantiated below. You can get the contents of the file.
    fn.call(
      module.exports,
      module,
      module.exports,
      req,
      module.id,
      path.dirname(module.id)
    );
  },
  ".json": function(module) {
    const jsonString = fs.readFileSync(module.id, "utf8");
    module.exports = JSON.parse(jsonString);
  }
};

Module.resolveFileName = function(filePath) {
  let absolutePath = path.resolve(__dirname, filePath);
  let flag = fs.existsSync(absolutePath); // Determine whether a file exists
  let current = absolutePath;
  if (!flag) {
    let keys = Object.keys(Module.extensions);
    for (let i = 0; i < keys.length; i++) {
      let combinePath = absolutePath + keys[i];
      let flag = fs.existsSync(combinePath); // Add file suffix before judging whether file exists.
      if (flag) {
        current = combinePath;
        break;
      } else {
        current = null;
      }
    }
  }
  if (!current) {
    throw new Error(`Current file does not exist ${current}`);
  }
  return current;
};

Module.prototype.load = function() {
  // this.id is the file path
  let extName = path.extname(this.id);
  Module.extensions[extName](this);
};

function req(filePath) {
  let absolutePath = Module.resolveFileName(filePath); // The absolute path to the file supports dynamic matching. js. JSON suffix
  let module = new Module(absolutePath);

  if (Module._cache[absolutePath]) {
    return Module._cache[absolutePath].exports;
  }

  // The value is a reference address. The real value comes when module.load() is executed
  Module._cache[absolutePath] = module;
  module.load(); // read file
  return module.exports;
}

I am a rookie at the front, welcome to read and point out mistakes!

Posted by NArc0t1c on Tue, 01 Oct 2019 07:57:17 -0700