How_Require_Extensions_Work
Why
Doing require extensions correctly is essential, because:
- Users should be able to install multiple extensions in succession, and have them work together.
- Coverage tools like
nycneed it to reliably supply coverage information that takes into account sourcemaps from upstream transforms. - Because non-standard, un-predictable behavior causes hard to solve bugs, and major headaches for project maintainers.
What is a require extension anyways?
First, it's worth remembering what default ".js" extension does.
require.extenstions['.js'] = function (module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(internalModule.stripBOM(content), filename);
}
Really simple. It reads the source content from the disk, and calls the module._compile. The default module._compile is a non-trivial piece of code, for simplicity I will just say it is what actually compiles the code.
Our first custom extension
Now let's install a transform that just appends the code + "bar" to the end of every file (humor me as I keep things simple).
This is how you would manually create that hook (in what is now widely accepted as the "right" way).
// append-bar.js
var oldHoook = require.extensions['.js']; // 1
require.extensions['.js'] = function (module, file) { // 2
var oldCompile = module._compile; // 3
module._compile = function (code, file) { // 4
code = code + ' + "bar"'; // 5
module._compile = oldCompile; // 6
module._compile(code + ' + "bar"'); // 7
};
oldHook(module, file); // 9
});
Note that this extension never reads from the disk. That is because the first extension in the chain (the system default one) handles loading from disk. If it's not obvious why that's true (it wasn't for me), keep reading.
The really important takeaway here is that you should be implementing require extensions almost exactly as I have above. There are multiple levels of indirection, and it can be confusing. Libraries like pirates can simplify the process.
Breakdown with 1 Custom Extension
Here is what happens when you call require("./foo.js")
// foo.js
module.exports = "foo"
What happens inside require boils down to this:
function pseudoRequire(filename) {
var ext = path.extname(filename); // ".js"
var module = new Module();
require.extensions[ext](module, filename);
}
Now let's step through the sequence of events.
- The system calls
require.extensions['.js'](module, './foo.js').
This meansappend-baris invoked with(module, './foo.js') append-barstores a reference tomodule._compile(line 3), an with its own wrapper function (line 4).
module._compilerefers to theappend-barwrapper function.
append-bar's reference tooriginalCompilerefers to the actual compile implementation.append-barcalls it'soldHook(the default.jsextension) with the modified module and filename (line 9).- The default
.jsextension reads in the source (module.exports = "foo"), and callsmodule._compile(source, filename).
Remembermodule._compilecurrently points to theappend-barwrapper function. - The append-bar wrapper adds
+ "bar"to the source (Line 5). The source is nowmodule.exports = "foo" + "bar". - The append-bar wrapper now replaces
module._compilewith it'soriginalCompilereference (Line 6).
module._compilenow points to the actual compile implementation module._compileis called again (this time pointing to actual, and the source is evaled and we get our result "foobar".
Breakdown with 2 Custom Extension
Assume we have first added the append-bar extension from above, followed by another called append-quz (which is for all purposes identical, except it appends baz instead.
- We install the
append-barextension (replacing the original hook)
append-bar#originalHookpoints to the original hook. - We install the
append-quzextension
append-quz#originalHookpoints to theappend-barhook. - We call
require('./foo.js'); append-quzhook is called with(module, './foo.js'), it replacesmodule._compilewith it's wrapper function.
append-quz#originalCompilepoints to the actual compile
module._compilepoints to theappend-quzwrapper.append-quzcalls it'soriginalHookreference, which isappend-bar.append-barreplacesmodule._compilewith it's wrapper.
append-bar#originalCompilepoints toappend-quzwrapper.
module._compilepoints to theappend-barwrapper.- The original extension is called, which loads the source from disk and calls
module._compile('module.exports = "foo"', './foo.js') - At this point
module._compilepoints to theappend-barwrapper, so the source is appended with+ "bar". - The
append-barcalls theoriginalCompilereference (which is theappend-quzwrapper). - The
append-quzwrapper does it's appending (so we've now got"foo" + "bar" + "quz") append-quzcalls it'soriginalCompilereference, which is actual and we get"foobarquz"
How_Require_Extensions_Work的更多相关文章
随机推荐
- Cracking The Coding Interview 3.6
// Write a program to sort a stack in ascending order. You should not make any assumptions about how ...
- CSS颜色
CSS 颜色 可以用以下方法来规定 CSS 中的颜色: · 十六进制色 十六进制值使用三个双位数来编写,并以 # 符号开头. · RGB 颜色 o 所有浏览器都支持 R ...
- nio的简单学习
参考链接: http://www.iteye.com/magazines/132-Java-NIO https://www.cnblogs.com/xiaoxi/p/6576588.html http ...
- matlabR2017安装
安装教程参考: https://blog.csdn.net/m0_37638031/article/details/78982498
- Vue 项目架构设计与工程化实践
来源 文中会讲述我从0~1搭建一个前后端分离的vue项目详细过程 Feature: 一套很实用的架构设计 通过 cli 工具生成新项目 通过 cli 工具初始化配置文件 编译源码与自动上传CDN Mo ...
- c#继承中的函数调用实例
using System; namespace Test { public class Base { public void Print() { ...
- Beta阶段冲刺---Day3
一.Daily Scrum Meeting照片 二.今天冲刺情况反馈 昨天已完成的工作: (1)数字以扑克牌的形式给出 (2)答案的乘除符号与游戏中的符号保持一致. 今天计划完成的工作 (1)闯关模式 ...
- three probing way of openadress hash
- Python 默认值字典
from collections import defaultdict # 默认值字典 dd = defaultdict(lambda: "胡辣汤") # callable 可调用 ...
- 【Python】sql-内连接,左连接,右连接,union
内连接: mysql> select * from book_wangjing as book_1 inner join user_wangjing as user_1 on book_1.id ...