使闭包编译器和Node.js发挥出色 [英] Getting closure-compiler and Node.js to play nice

查看:112
本文介绍了使闭包编译器和Node.js发挥出色的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有任何项目一起使用过node.js和closure-compiler(简称CC)?

CC的官方建议是一起编译应用程序的所有代码,但是当我编译一些包含require("./MyLib.js")的简单node.js代码时,该行将直接放入输出中,但不会产生任何影响在这种情况下有意义.

我看到一些选择:

  1. 将整个应用程序编码为单个文件.这样可以避免这种情况,从而解决了问题,但对维护不利.
  2. 假定所有文件在执行前都将被串联.再次避免了该问题,但是使实现未编译的调试模式更加困难.
  3. 我想让CC来理解" node.js require()函数,但是如果不编辑编译器本身就无法做到这一点,可以吗?

解决方案

我一直在对尚未发布的项目使用带有节点的Closure编译器.它花费了一些工具,但它帮助捕获了许多错误,并且编辑-重启-测试周期非常短.

首先,我使用 plovr (这是我创建并维护的项目),以便使用Closure编译器,库和模板一起.我以Closure库的样式编写了Node代码,因此每个文件都定义了自己的类或实用程序集合(如goog.array).

下一步是为要使用的Node函数创建一堆externs文件.我在以下位置公开发布了其中一些内容:

https://github.com/bolinfest /node-google-closure-latitude-experiment/tree/master/externs/node/v0.4.8

尽管最终,我认为这应该是更受社区驱动的事情,因为其中有很多功能需要记录. (这也很烦人,因为某些Node函数具有可选的中间参数而不是最后一个参数,从而使类型注释变得复杂.)我自己还没有开始这一运动,因为我们有可能可以对Closure Complier进行一些工作,以使它不那么尴尬(请参见下文).

假设您已经为Node名称空间http创建了externs文件.在我的系统中,我已经决定,只要需要http,我都会通过以下方式将其包括进来:

var http = require('http');

尽管我没有在代码中包含该require()调用.相反,我使用Closure Compiler的output-wrapper功能将所有require()放在文件的开头,当在plovr中声明时,在我当前的项目中看起来像这样:

"output-wrapper": [
  // Because the server code depends on goog.net.Cookies, which references the
  // global variable "document" when instantiating goog.net.cookies, we must
  // supply a dummy global object for document.
  "var document = {};\n",

  "var bee = require('beeline');\n",
  "var crypto = require('crypto');\n",
  "var fs = require('fs');\n",
  "var http = require('http');\n",
  "var https = require('https');\n",
  "var mongodb = require('mongodb');\n",
  "var nodePath = require('path');\n",
  "var nodeUrl = require('url');\n",
  "var querystring = require('querystring');\n",
  "var SocketIo = require('socket.io');\n",
  "%output%"
],

这样,我的库代码从不调用Node的require(),但是编译器允许在我的代码中使用诸如http之类的东西,因为编译器将它们识别为外部代码.由于它们不是真正的外部元素,因此必须按照我的描述进行添加.

最终,在讨论列表上讨论了这个之后,我认为更好的解决方案是为名称空间添加一个新的类型注释,如下所示:

goog.scope(function() {

    /** @type {~NodeHttpNamesapce} */
    var http = require('http');

    // Use http throughout.

});

在这种情况下,一个externs文件将定义NodeHttpNamespace,以便Closure Compiler能够使用externs文件对它的属性进行类型检查.此处的区别在于您可以根据需要命名require()的返回值,因为http的类型将是这种特殊的名称空间类型. (为$标识一个"jQuery命名空间"是一个类似的问题.)这种方法将消除对一致地为Node命名空间命名局部变量的需要,并消除了在plovr配置中对该巨型output-wrapper的需求. /p>

但这是题外话...一旦我如上所述进行设置,我就有一个shell脚本:

  1. 使用plovr以RAW模式构建所有内容.
  2. 在plovr生成的文件上运行node.

使用RAW模式会导致所有文件的串联(尽管它还需要将Soy模板,甚至将CoffeeScript转换为JavaScript).诚然,这使调试很麻烦,因为行号是胡说八道,但是到目前为止,对于我来说,它已经足够好了. Closure编译器执行的所有检查都值得这样做.

Are there any projects that used node.js and closure-compiler (CC for short) together?

The official CC recommendation is to compile all code for an application together, but when I compile some simple node.js code which contains a require("./MyLib.js"), that line is put directly into the output, but it doesn't make any sense in that context.

I see a few options:

  1. Code the entire application as a single file. This solves the problem by avoiding it, but is bad for maintenance.
  2. Assume that all files will be concatenated before execution. Again this avoids the problem, but makes it harder to implement a un-compiled debug mode.
  3. I'd like to get CC to "understand" the node.js require() function, but that probably can't be done without editing the compiler itself, can it?

解决方案

I have been using the Closure Compiler with Node for a project I haven't released yet. It has taken a bit of tooling, but it has helped catch many errors and has a pretty short edit-restart-test cycle.

First, I use plovr (which is a project that I created and maintain) in order to use the Closure Compiler, Library, and Templates together. I write my Node code in the style of the Closure Library, so each file defines its own class or collection of utilities (like goog.array).

The next step is to create a bunch of externs files for the Node functions you want to use. I published some of these publicly at:

https://github.com/bolinfest/node-google-closure-latitude-experiment/tree/master/externs/node/v0.4.8

Though ultimately, I think that this should be a more community driven thing because there are a lot of functions to document. (It's also annoying because some Node functions have optional middle arguments rather than last arguments, making the type annotations complicated.) I haven't started this movement myself because it's possible that we could do some work with the Closure Complier to make this less awkward (see below).

Say you have created the externs file for the Node namespace http. In my system, I have decided that anytime I need http, I will include it via:

var http = require('http');

Though I do not include that require() call in my code. Instead, I use the output-wrapper feature of the Closure Compiler the prepend all of the require()s at the start of the file, which when declared in plovr, in my current project looks like this:

"output-wrapper": [
  // Because the server code depends on goog.net.Cookies, which references the
  // global variable "document" when instantiating goog.net.cookies, we must
  // supply a dummy global object for document.
  "var document = {};\n",

  "var bee = require('beeline');\n",
  "var crypto = require('crypto');\n",
  "var fs = require('fs');\n",
  "var http = require('http');\n",
  "var https = require('https');\n",
  "var mongodb = require('mongodb');\n",
  "var nodePath = require('path');\n",
  "var nodeUrl = require('url');\n",
  "var querystring = require('querystring');\n",
  "var SocketIo = require('socket.io');\n",
  "%output%"
],

In this way, my library code never calls Node's require(), but the Compiler tolerates the uses of things like http in my code because the Compiler recognizes them as externs. As they are not true externs, they have to be prepended as I described.

Ultimately, after talking about this on the discussion list, I think the better solution is to have a new type annotation for namespaces that would look something like:

goog.scope(function() {

    /** @type {~NodeHttpNamesapce} */
    var http = require('http');

    // Use http throughout.

});

In this scenario, an externs file would define the NodeHttpNamespace such that the Closure Compiler would be able to typecheck properties on it using the externs file. The difference here is that you could name the return value of require() whatever you wanted because the type of http would be this special namespace type. (Identifying a "jQuery namespace" for $ is a similar issue.) This approach would eliminate the need to name your local variables for Node namespaces consistently, and would eliminate the need for that giant output-wrapper in the plovr config.

But that was a digression...once I have things set up as described above, I have a shell script that:

  1. Uses plovr to build everything in RAW mode.
  2. Runs node on the file generated by plovr.

Using RAW mode results in a large concatenation of all the files (though it also takes care of translating Soy templates and even CoffeeScript to JavaScript). Admittedly, this makes debugging a pain because the line numbers are nonsense, but has been working well enough for me so far. All of the checks performed by the Closure Compiler have made it worth it.

这篇关于使闭包编译器和Node.js发挥出色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆