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. win8 下面 listen 的队列长度貌似无效了 上c/s 代码 并附截图,有图有真相

    #include <WinSock2.h> #include <stdio.h> #include <windows.h> DWORD ServerRoutine( ...

  2. Capjoint的merrcmd生成二次曲线的misfit原理

    http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html

  3. h5页面嵌入android app时遇到的问题

    1.h5页面 通过 .css("transform") 或 .style.transform 获取 transform属性,并通过 split 方法解析 页面translateY ...

  4. shell脚本实例-nginx日志分析

    统计2018/8/6 PV量 grep "06/Aug/2018" access.log|wc -l 统计当天8:00 到9:00的PV awk '$4>="[06 ...

  5. C#窗体换肤

    Form1.cs using System;using System.Collections.Generic;using System.ComponentModel;using System.Data ...

  6. python Django rest-framework 创建序列化工程步骤

    11创建项目 2创建应用 3stting添加应用(apps)-添加制定数据库-修改显示汉字(zh-hans)-上海时区(Asia/Shanghai) 4主路由添加子路由 5应用里创建子路由 6创建数据 ...

  7. python点滴:读取和整合文件夹下的所有文件

    当我们想读取一个文件夹下的多个文件,并且将所有文件的内容整合成一个文件,应该怎么做? 基本的思路是:写一个专门的函数实现以上两个功能.主要用到的命令包括os.listdir().codecs.open ...

  8. JS执行机制--事件循环--笔记

    JS的解析是由浏览器中的JS解析引擎完成的.JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始.但是又存在某些任务比较耗时,如IO读写等, ...

  9. 2017第八届蓝桥杯C/C++ B组省赛-购物单

    标题: 购物单 小明刚刚找到工作,老板人很好,只是老板夫人很爱购物.老板忙的时候经常让小明帮忙到商场代为购物.小明很厌烦,但又不好推辞. 这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有打折 ...

  10. spark-streaming first insight

    一. Spark Streaming 构建在Spark core API之上,具备可伸缩,高吞吐,可容错的流处理模块. 1)支持多种数据源,如Kafka,Flume,Socket,文件等: Basic ...