Last active
April 1, 2021 08:53
-
-
Save GoodbyeNJN/49329ce8ba4d4a3630cc96d8f8a776f5 to your computer and use it in GitHub Desktop.
Node.js CommonJS Module 实现示例
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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