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的更多相关文章
随机推荐
- 谷歌浏览器慎用有道词典插件(<audio></audio>) (转载)
谷歌浏览器慎用有道词典插件(<audio></audio>) 原文 :http://blog.csdn.net/u010556394/article/details/7112 ...
- 第三节 java 函数的封装方法 以及 访问封装内容
从我们的选择排序和冒泡排序里我们可以看到有很多相同的代码, 我们 可以把这些相同的代码提取出来封装为方法:比如我们的判 断交换和遍历输出: 抽取1: public static void PanDua ...
- Android开发 ---如何操作资源目录中的资源文件5 ---Raw资源管理与国际化
效果图: 1.activity_main.xml 描述: 定义两个按钮,一个是Raw资源管理,一个是处理国际化语言,其中i18n表示简体中文 <?xml version="1.0&qu ...
- GMT5.4.2 installation
具体安装步骤参考seisman github gmt5.4.2或是GMT中文社区上的说明:这里只是补充一些可能出现问题的解决方法: 需要将具神标注的可选包也都安装上. 将cmake中的configue ...
- Alpha冲刺2
前言 队名:拖鞋旅游队 组长博客:https://www.cnblogs.com/Sulumer/p/9960487.html 作业博客:https://edu.cnblogs.com/campus/ ...
- Linux平台 获取程序依赖文件
创建sh脚本文件 =========================================================== #!/bin/sh exe=$1 #发布的程序名称des=$2 ...
- 图片 100%显示. img 全部显示.
让每个图片 都铺满 ,同样的大小; 只要给 img 设置 固定的高度, 宽度就可以 了. ----------------------- html: <div class="co ...
- ios九宫格算法
- (void)viewDidLoad { [super viewDidLoad]; //1.总列数(一行最多3列) ; CGFloat appW=; CGFloat appH=; //2.间隙=(控 ...
- Day17作业及默写
正则表达式练习 1.匹配一篇英文文章的标题 类似 The Voice Of China ([A-Z][a-z]*)( [A-Z][a-z]*)* 2.匹配一个网址 https://www.baidu. ...
- 效率生产力工具 —— idea 插件
maven helper: 打开该pom文件的Dependency Analyzer视图(在文件打开之后,文件下面会多出这样一个tab), 进入Dependency Analyzer视图之后有三个查看 ...