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的更多相关文章
随机推荐
- win8 下面 listen 的队列长度貌似无效了 上c/s 代码 并附截图,有图有真相
#include <WinSock2.h> #include <stdio.h> #include <windows.h> DWORD ServerRoutine( ...
- Capjoint的merrcmd生成二次曲线的misfit原理
http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html
- h5页面嵌入android app时遇到的问题
1.h5页面 通过 .css("transform") 或 .style.transform 获取 transform属性,并通过 split 方法解析 页面translateY ...
- shell脚本实例-nginx日志分析
统计2018/8/6 PV量 grep "06/Aug/2018" access.log|wc -l 统计当天8:00 到9:00的PV awk '$4>="[06 ...
- C#窗体换肤
Form1.cs using System;using System.Collections.Generic;using System.ComponentModel;using System.Data ...
- python Django rest-framework 创建序列化工程步骤
11创建项目 2创建应用 3stting添加应用(apps)-添加制定数据库-修改显示汉字(zh-hans)-上海时区(Asia/Shanghai) 4主路由添加子路由 5应用里创建子路由 6创建数据 ...
- python点滴:读取和整合文件夹下的所有文件
当我们想读取一个文件夹下的多个文件,并且将所有文件的内容整合成一个文件,应该怎么做? 基本的思路是:写一个专门的函数实现以上两个功能.主要用到的命令包括os.listdir().codecs.open ...
- JS执行机制--事件循环--笔记
JS的解析是由浏览器中的JS解析引擎完成的.JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始.但是又存在某些任务比较耗时,如IO读写等, ...
- 2017第八届蓝桥杯C/C++ B组省赛-购物单
标题: 购物单 小明刚刚找到工作,老板人很好,只是老板夫人很爱购物.老板忙的时候经常让小明帮忙到商场代为购物.小明很厌烦,但又不好推辞. 这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有打折 ...
- spark-streaming first insight
一. Spark Streaming 构建在Spark core API之上,具备可伸缩,高吞吐,可容错的流处理模块. 1)支持多种数据源,如Kafka,Flume,Socket,文件等: Basic ...