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的更多相关文章
随机推荐
- 使用GCD控制网络请求
当,当山峰没有棱角的时候 当河水不再流 当时间停住日夜不分 当天地万物化为虚有!,,,,不好意思跑题了! 当我们在一个页面中需要进行多次网络请求才能满足页面所有的显示需要的时候,我们需要控制这些请求全 ...
- 十八. Python基础(18)常用模块
十八. Python基础(18)常用模块 1 ● 常用模块及其用途 collections模块: 一些扩展的数据类型→Counter, deque, defaultdict, namedtuple, ...
- bootstrap-select 下拉多选组件
<div class="form-group"> <label class="col-lg-2 col-sm-2 control-label" ...
- Centos7 LNMP 一键安装
首页: https://lnmp.org/ 安装包生成页: https://lnmp.org/auto.html
- 使用MyEclipse开发Java EE应用:用XDoclet创建EJB 2 Session Bean项目(一)
[MyEclipse最新版下载] 一.创建一个EJB项目 1. 选择File>New Project,选择EJB Project,然后单击Next. 2. 在Project name字段中输入f ...
- javascript性能优化之使用对象、数组直接量代替典型的对象创建和赋值
1.典型的对象创建和赋值操作代码示例 var myObject = new Object(); myObject.name = "Nicholas"; myObject.count ...
- Delphi 10.3.1 TNetHttpClient在多线程中存在的问题及解决方法。
Delphi 10.3.1发布了,对10.3.0存在的各种问题,做了大量的修正.但听高勇说TNetHttpClient在多线程中存在问题,今天做了一下测试,确实如此,看来,还需要官方进一步修正! 具体 ...
- 什么是wsgi,uwsgi,uWSGI
WSGI: web服务器网关接口,是一套协议.用于接收用户请求将请求进行初次封装,然后将请求交给web框架 实现wsgi协议的模块: 1,wsgiref,本质就是编写一个socket服务端,用于接收用 ...
- python模块大全
python模块大全2018年01月25日 13:38:55 mcj1314bb 阅读数:3049 pymatgen multidict yarl regex gvar tifffile jupyte ...
- 使用scrapy ImagesPipeline爬取图片资源
这是一个使用scrapy的ImagesPipeline爬取下载图片的示例,生成的图片保存在爬虫的full文件夹里. scrapy startproject DoubanImgs cd DoubanIm ...