babel原理分析-babel-register addHook

babel原理分析-babel-register addHook

前言

阅读本文时但愿您对babel-register有必定了解,若是还有不了解的能够阅读以前的文章传送门node

在以前的文章中已经简单介绍了babel-register的功能es6

那么babel如何给require加上钩子,使得在node环境下实现动态编译的呢(静态编译:统一babel。动态编译:js编译到这行的时候进行编译)npm

原理分析

注:本文参考代码为babel-0.7.0-beta版本代码。segmentfault

其实在上文babel-register中能够看到,node环境下babel的编译,是经过一个require上addHook的解决方法,那么这个hook是怎么挂载到require上的呢babel

首先想到的是node官版有没有提供原生的方法处理,官版确实提供了一个require.extensions的方法,惋惜已经废弃了,moudle模块也没有所谓addhook的办法。那就只能安静点看pirate的实现了less

深刻pirate的源码时,咱们却发现实际pirate这个npm包并无作什么功能ui

核心代码不超过100行,以下this

const Module = module.constructor.length > 1
  ? module.constructor
  : BuiltinModule;

export function addHook(hook, opts = {}) { // eslint-disable-line import/prefer-default-export
  let reverted = false;
  const loaders = [];
  const oldLoaders = [];
  let exts;

  const originalJSLoader = Module._extensions['.js'];

  const matcher = opts.matcher || null;
  const ignoreNodeModules = opts.ignoreNodeModules !== false;
  exts = opts.extensions || opts.exts || opts.extension || opts.ext || ['.js'];
  if (!Array.isArray(exts)) exts = [exts];

  exts.forEach((ext) => {
    if (typeof ext !== 'string') throw new TypeError(`Invalid Extension: ${ext}`);
    const oldLoader = Module._extensions[ext] || originalJSLoader;
    oldLoaders[ext] = oldLoader;

    loaders[ext] = Module._extensions[ext] = function newLoader(mod, filename) {
      let compile;
      if (!reverted) {
        if (shouldCompile(filename, exts, matcher, ignoreNodeModules)) {
          compile = mod._compile;
          mod._compile = function _compile(code) {
            mod._compile = compile;
            const newCode = hook(code, filename);
            if (typeof newCode !== 'string') {
              throw new Error(HOOK_RETURNED_NOTHING_ERROR_MESSAGE);
            }

            return mod._compile(newCode, filename);
          };
        }
      }

      oldLoader(mod, filename);
    };
  });
  return function revert() {
    if (reverted) return;
    reverted = true;

    exts.forEach((ext) => {
      if (Module._extensions[ext] === loaders[ext]) {
        Module._extensions[ext] = oldLoaders[ext];
      }
    });
  };
}

看起来这个代码作的事很简单就是在给原生moudle方法上不断地挂载moudle._extension['.js/.es6/.jsx']之类的处理func,始终没有看到执行时机。spa

因而简单写了一个demodebug

console.log('naturelessTT')

debugger;

require('require.js')

babel-node index.js --inspect-brk运行去看这个hook的执行时机以及call stack。

实际是require文件时,io读取文件后会经过moudle.load的方法加载文件,而后依次执行_extension里挂载的方法

真相大白,可是使人惊讶的是0.7以前的版本并无引入pirate这个包,看了0.6.26版本后,emmmm,babel大佬使用了官版已经标记为废弃的require.extensions。

固然虽然是废弃的,可是node这个模块已经锁死,因此babel大佬还在肆无忌惮的用,固然0.7已经作了修正

Deprecated 

In the past, this list has been used to load non-JavaScript modules into Node.js by compiling them on-demand. However, in practice, there are much better ways to do this, such as loading modules via some other Node.js program, or compiling them to JavaScript ahead of time.

Since the module system is locked, this feature will probably never go away. However, it may have subtle bugs and complexities that are best left untouched.

Note that the number of file system operations that the module system has to perform in order to resolve a require(...) statement to a filename scales linearly with the number of registered extensions.

In other words, adding extensions slows down the module loader and should be discouraged.
相关文章
相关标签/搜索