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. 谷歌浏览器慎用有道词典插件(<audio></audio>) (转载)

    谷歌浏览器慎用有道词典插件(<audio></audio>)  原文 :http://blog.csdn.net/u010556394/article/details/7112 ...

  2. 第三节 java 函数的封装方法 以及 访问封装内容

    从我们的选择排序和冒泡排序里我们可以看到有很多相同的代码, 我们 可以把这些相同的代码提取出来封装为方法:比如我们的判 断交换和遍历输出: 抽取1: public static void PanDua ...

  3. Android开发 ---如何操作资源目录中的资源文件5 ---Raw资源管理与国际化

    效果图: 1.activity_main.xml 描述: 定义两个按钮,一个是Raw资源管理,一个是处理国际化语言,其中i18n表示简体中文 <?xml version="1.0&qu ...

  4. GMT5.4.2 installation

    具体安装步骤参考seisman github gmt5.4.2或是GMT中文社区上的说明:这里只是补充一些可能出现问题的解决方法: 需要将具神标注的可选包也都安装上. 将cmake中的configue ...

  5. Alpha冲刺2

    前言 队名:拖鞋旅游队 组长博客:https://www.cnblogs.com/Sulumer/p/9960487.html 作业博客:https://edu.cnblogs.com/campus/ ...

  6. Linux平台 获取程序依赖文件

    创建sh脚本文件 =========================================================== #!/bin/sh exe=$1 #发布的程序名称des=$2 ...

  7. 图片 100%显示. img 全部显示.

    让每个图片 都铺满 ,同样的大小;    只要给 img 设置 固定的高度, 宽度就可以 了. ----------------------- html: <div class="co ...

  8. ios九宫格算法

    - (void)viewDidLoad { [super viewDidLoad]; //1.总列数(一行最多3列) ; CGFloat appW=; CGFloat appH=; //2.间隙=(控 ...

  9. Day17作业及默写

    正则表达式练习 1.匹配一篇英文文章的标题 类似 The Voice Of China ([A-Z][a-z]*)( [A-Z][a-z]*)* 2.匹配一个网址 https://www.baidu. ...

  10. 效率生产力工具 —— idea 插件

    maven helper: 打开该pom文件的Dependency Analyzer视图(在文件打开之后,文件下面会多出这样一个tab), 进入Dependency Analyzer视图之后有三个查看 ...