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
nyc
need 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-bar
is invoked with(module, './foo.js')
append-bar
stores a reference tomodule._compile
(line 3), an with its own wrapper function (line 4).
module._compile
refers to theappend-bar
wrapper function.
append-bar
's reference tooriginalCompile
refers to the actual compile implementation.append-bar
calls it'soldHook
(the default.js
extension) with the modified module and filename (line 9).- The default
.js
extension reads in the source (module.exports = "foo"
), and callsmodule._compile(source, filename)
.
Remembermodule._compile
currently points to theappend-bar
wrapper 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._compile
with it'soriginalCompile
reference (Line 6).
module._compile
now points to the actual compile implementation module._compile
is 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-bar
extension (replacing the original hook)
append-bar#originalHook
points to the original hook. - We install the
append-quz
extension
append-quz#originalHook
points to theappend-bar
hook. - We call
require('./foo.js');
append-quz
hook is called with(module, './foo.js')
, it replacesmodule._compile
with it's wrapper function.
append-quz#originalCompile
points to the actual compile
module._compile
points to theappend-quz
wrapper.append-quz
calls it'soriginalHook
reference, which isappend-bar
.append-bar
replacesmodule._compile
with it's wrapper.
append-bar#originalCompile
points toappend-quz
wrapper.
module._compile
points to theappend-bar
wrapper.- The original extension is called, which loads the source from disk and calls
module._compile('module.exports = "foo"', './foo.js')
- At this point
module._compile
points to theappend-bar
wrapper, so the source is appended with+ "bar"
. - The
append-bar
calls theoriginalCompile
reference (which is theappend-quz
wrapper). - The
append-quz
wrapper does it's appending (so we've now got"foo" + "bar" + "quz"
) append-quz
calls it'soriginalCompile
reference, which is actual and we get"foobarquz"
How_Require_Extensions_Work的更多相关文章
随机推荐
- import 语句
声明package的语句必须在java类的有效代码第一行,所import语句要放在package 声明语句之后. import的语法格式为: import+空格+类全限定名+: 该语句的作用是, ...
- Linux分区和挂载的理解
在工作中经常使用到Linux,对分区和挂载的概念一直都很模糊,对网上的信息进行了整理,方便理解. 1为什么要分区(需理解硬盘的组成) 1)数据的安全性,因为每个分区的数据是分开的.所以,当你需要将某个 ...
- mysql创建存储过程,定时任务,定时删除log
-- 创建存储过程 清除30天前的日志create procedure deleteLog()BEGINdelete from contract_vlog where create_time<D ...
- UNITY3d在移动设备上的一些优化实战
项目进入了中期之后,就需要对程序在移动设备上的表现做分析评估和针对性的优化了,首先前期做优化,很多瓶颈没表现出来,能做的东西不多,而且很多指标会凭预想,如果太后期做优化又会太晚,到时发现一些问题改起来 ...
- <Google><APAC><kickstart><2017.05.07><2017RoundB>
Google APAC kickstart 网址链接 我的所有solution代码和文件请点击 前言 这个比赛的题怎一个变态了得,虽然是第一次参赛,抱着熟悉流程的心态去的,但仍然被虐得一颤一颤的╮(╯ ...
- JavaWeb:动作标识
动作标识 一.包含文件标识<jsp:include> 1.介绍 用于向当前页面中包含其他文件.被包含的文件可以是动态文件,也可以是静态文件 2.语法格式 <jsp:include p ...
- java学习笔记6(面向对象1:概念,private)
1.思想: 面向过程的思想:遇到问题时想,我该如何做,然后分步骤实现: 面向对象的思想:遇到问题时想,我该派谁去做这件事,至于他怎么做,与我无关,我只要最后的结果. 实际举例:我们要组装一台电脑: 面 ...
- natapp 穿透访问 vue项目 Invalid Host header
由于要近期开发微信小程序,所以今天了解了一下这个netapp 内网映射这个东西,所以一开始自己就在网上看,然后想把环境部署起来,参考https://natapp.cn/ ,看了一分钟教程以后,然后自己 ...
- 6--Python入门--Python基本运算符
算数运算符 运算符 描述 示例 + 相加 1+1→2 - 相减 1-1→0 * 相乘 1*2→2 / 相除 1/2→0.5 % 取余数 3%2→1 ** 幂运算 2**2→4 // 取商 7//2→3 ...
- Web安全测试解决方案
Web安全测试解决方案 介绍常见的Web安全风险,Web安全测试方法.测试基本理论和测试过程中的工具引入