Why

Doing require extensions correctly is essential, because:

  1. Users should be able to install multiple extensions in succession, and have them work together.
  2. Coverage tools like nyc need it to reliably supply coverage information that takes into account sourcemaps from upstream transforms.
  3. 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.

  1. The system calls require.extensions['.js'](module, './foo.js').
    This means append-bar is invoked with (module, './foo.js')
  2. append-bar stores a reference to module._compile (line 3), an with its own wrapper function (line 4).
    module._compile refers to the append-bar wrapper function.
    append-bar's reference to originalCompile refers to the actual compile implementation.
  3. append-bar calls it's oldHook (the default .js extension) with the modified module and filename (line 9).
  4. The default .js extension reads in the source (module.exports = "foo"), and calls module._compile(source, filename).
    Remember module._compile currently points to the append-bar wrapper function.
  5. The append-bar wrapper adds + "bar" to the source (Line 5). The source is now module.exports = "foo" + "bar".
  6. The append-bar wrapper now replaces module._compile with it's originalCompile reference (Line 6).
    module._compile now points to the actual compile implementation
  7. 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.

    1. We install the append-bar extension (replacing the original hook)
      append-bar#originalHook points to the original hook.
    2. We install the append-quz extension
      append-quz#originalHook points to the append-bar hook.
    3. We call require('./foo.js');
    4. append-quz hook is called with (module, './foo.js'), it replaces module._compile with it's wrapper function.
      append-quz#originalCompile points to the actual compile
      module._compile points to the append-quz wrapper.
    5. append-quz calls it's originalHook reference, which is append-bar.
    6. append-bar replaces module._compile with it's wrapper.
      append-bar#originalCompile points to append-quz wrapper.
      module._compile points to the append-bar wrapper.
    7. The original extension is called, which loads the source from disk and calls module._compile('module.exports = "foo"', './foo.js')
    8. At this point module._compile points to the append-bar wrapper, so the source is appended with + "bar".
    9. The append-bar calls the originalCompile reference (which is the append-quz wrapper).
    10. The append-quz wrapper does it's appending (so we've now got "foo" + "bar" + "quz")
    11. append-quz calls it's originalCompile reference, which is actual and we get "foobarquz"

How_Require_Extensions_Work的更多相关文章

随机推荐

  1. Cracking The Coding Interview2.4

    删除前面的linklist,使用node来表示链表 // You have two numbers represented by a linked list, where each node cont ...

  2. 单元测试模拟-moq

    1.moq 支持 net core 2.moq 通过一个接口类型 可以产生一个新的类 3.举例 //define interface to be mocked public interface ITe ...

  3. org.quartz-scheduler 动态添加自动任务

    1.添加pom.xml <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId> ...

  4. C# 连接EXCEL 和 ACCESS

    97-2003版本 EXCEL Provider=Microsoft.Jet.OLEDB.4.0;Data Source=文件位置;Extended Properties=Excel 8.0;HDR= ...

  5. 十、编写LED混杂设备驱动

    led.c修改为: #include <linux/init.h> #include <linux/module.h> #include <linux/miscdevic ...

  6. 用StringHelper.Split分解字符串

    StringHelper提供了大量的方法,从而用链试写法处理字符串,实现对字符串的各种操作.比如: var s1,s2:string; begin s1:='abcdefg'; s2:=s1.subs ...

  7. ​插件化DPI在商用WIFI中的价值

    插件化DPI是指DPI(深度包检测)技术以插件/模块的方式嵌入到各种网络设备中,是一种新型轻量级资源消耗的互联网技术,由迈科网络(股份代码:430575)独创开发. 插件化DPI(深度包检测)技术服务 ...

  8. 2016ICPC-大连 A Simple Math Problem (数学)

    Given two positive integers a and b,find suitable X and Y to meet the conditions: X+Y=a Least Common ...

  9. Spring Relational Database

    为了避免持久化的逻辑分散到应用的各个组件中,将数据访问功能放到一个或多个专注于此项任务的组件中,这样的组件通常称为数据访问对象(DAO)或Repository. 为了避免应用与特定的数据访问策略耦合在 ...

  10. 基于链路的OSPFMD5口令认证

    实验要求:掌握基于链路的OSPFMD5口令认证 拓扑如下: 配置如下: R1enable configure terminal interface s0/0/0ip address 192.168.1 ...