Skip to content

Instantly share code, notes, and snippets.

@GoodbyeNJN
Last active April 1, 2021 08:53
Show Gist options
  • Select an option

  • Save GoodbyeNJN/49329ce8ba4d4a3630cc96d8f8a776f5 to your computer and use it in GitHub Desktop.

Select an option

Save GoodbyeNJN/49329ce8ba4d4a3630cc96d8f8a776f5 to your computer and use it in GitHub Desktop.
Node.js CommonJS Module 实现示例
const path = require("path");
const vm = require("vm");
const fs = require("fs");
const cache = Object.create(null);
const extensions = Object.create(null, {
".js": {
value: (module, filename) => {
const content = fs.readFileSync(filename, "utf-8");
module._compile(content, filename);
},
},
".json": {
value: (module, filename) => {
const content = fs.readFileSync(filename, "utf-8");
module.exports = JSON.parse(content);
},
},
});
const wrap = (code) => `(function (exports, require, module, __filename, __dirname) { ${code} });`;
const resolveFilename = (request) => {
const filename = path.resolve(request);
const extname = path.extname(request);
if (!extname) {
for (const ext of Object.keys(extensions)) {
const filepath = `${filename}${ext}`;
if (fs.existsSync(filepath)) {
return filepath;
}
}
}
return filename;
};
const load = (request) => {
const filename = resolveFilename(request);
// 命中缓存就直接返回,不再加载模块
const cachedModule = cache[filename];
if (cachedModule) {
return cachedModule.exports;
}
const module = new Module(filename);
cache[filename] = module; // 缓存模块
module.load();
return module.exports;
};
class Module {
constructor(id = "") {
this.id = id;
this.path = path.dirname(id);
this.exports = {};
this.filename = null;
this.loaded = false;
}
static _cache = cache;
static _extensions = extensions;
static wrap = wrap;
static _resolveFilename = resolveFilename;
static _load = load;
require(id) {
return load(id);
}
load() {
this.filename = filename;
const extname = path.extname(this.filename);
extensions[extname](this, this.filename);
this.loaded = true;
}
_compile(content) {
const wrapped = wrap(content);
// 在当前 global 对象的上下文中编译并执行 code,最后返回结果
// 返回值是一个可调用的函数
const compiled = vm.runInThisContext(wrapped, { filename: this.filename });
const dirname = path.dirname(this.filename);
// 调用并传入四个参数
Reflect.apply(compiled, this.exports, [
this.exports,
this.require,
this,
this.filename,
dirname,
]);
}
}
exports = module.exports = Module;
// 用法
// Module._load("xxx.js");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment