How do you define node_modules as externs in Closure Compiler?

那年仲夏 提交于 2019-11-28 23:40:25

The issue is that you wish for the compiler to somehow recognize that certain require calls are internal, namely that the required module should be processed by the compiler as source, and others are external so should be left alone. There isn't a good way to handle this situation currently.

Workarounds

Use Post-processing to Add External Require Statements

In this scenario you would completely omit any require statements to external modules. The compiler would only process code with internal require statements and modules. After compilation, you would prepend the external require statements:

Header JS To Be Prepended

var crypto = require('crypto');

Source To Be Compiled

console.log(crypto);

Because crypto is declared in an extern, the compiler will correctly recognize the type and symbol name.

Alias Require Calls

When the --process_common_js_modules is specified, the compiler recognizes require statements and expands them in a similar fashion to the way macros work in other languages. By aliasing the require statements that should remain external, the compiler will not recognize them and thus not expand them.

Source To Be Compiled

var externalRequire = require;
/** @suppress {duplicate} this is already defined in externs */
var crypto = externalRequire('crypto');
console.log(crypto)

If you're using the Closure Compiler only for type-checking—i.e., with the --checks-only option—there is another workaround which has the advantage (over the ones mentioned in Chad's answer) of working correctly with unmodified third-party NPM modules which in turn import built-in modules.

Using Stubs

The trick is to create stub NPM modules to stand in for the built-in ones. These can be minimal; they only need to declare the parts of the API you're actually using.

Here's an example for the path built-in module.

In externs/path/path.js I have the "externs" declarations (not actually externs, so e.g. you can't use @nosideeffects) for the part of path that I need:

/** @const */
var path = {};

/**
 * @param {string} path
 * @return {string}
 */
path.dirname = function(path) {};

/**
 * @param {string} path
 * @return {string}
 */
path.extname = function(path) {};

/**
 * @param {...string} var_args
 * @return {string}
 */
path.join = function(var_args) {};

module.exports = path;

In externs/path/package.json, I have a minimal NPM package config:

{
  "description": "Fake package.json for require('path')",
  "main": "path.js",
  "name": "path",
}

Then create a symlink from node_modules/path to externs/path, and add the following to my compiler flags:

node_modules/path/package.json
node_modules/path/path.js

(You could put the stub implementation directly in to node_modules but I prefer to keep my stubs separate from the real modules managed by npm. I just need to remember to manually add the symlinks to my Git repo, because it's otherwise configured to ignore node_modules.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!