繁体   English   中英

让closure-compiler和Node.js发挥得很好

[英]Getting closure-compiler and Node.js to play nice

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

官方的CC建议是一起编译应用程序的所有代码,但是当我编译一些包含require("./MyLib.js")简单node.js代码时,该行直接放入输出中,但它没有在这种背景下没有任何意义。

我看到几个选项:

  1. 将整个应用程序编码为单个文件。 这通过避免它来解决问题,但是对于维护是不利的。
  2. 假设在执行之前将连接所有文件。 这又避免了这个问题,但却使实现未编译的调试模式变得更加困难。
  3. 我想让CC“理解”node.js require()函数,但如果不编辑编译器本身就可能无法完成,可以吗?

我一直在使用Closure Compiler with Node来完成我尚未发布的项目。 它采用了一些工具,但它有助于捕获许多错误并且具有非常短的编辑 - 重启 - 测试周期。

首先,我使用plovr (这是我创建和维护的项目),以便一起使用Closure Compiler,Library和Templates。 我以Closure Library的样式编写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这样的东西,因为编译器将它们识别为externs。 由于它们不是真正的外部因素,因此必须按照我的描述进行。

最后,在讨论列表中讨论了这个之后,我认为更好的解决方案是为命名空间创建一个类型的新类型注释:

goog.scope(function() {

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

    // Use http throughout.

});

在这种情况下,externs文件将定义NodeHttpNamespace ,以便Closure Compiler能够使用externs文件对其进行类型检查。 这里的不同之处在于,无论您想要什么,都可以将require()的返回值命名为,因为http的类型将是这种特殊的命名空间类型。 (为$确定一个“jQuery名称空间”是一个类似的问题。)这种方法将不再需要为Node命名空间命名本地变量,并且不需要在plovr配置中使用那个巨大的output-wrapper

但这是一个题外话......一旦我按照上面所述设置了东西,我就有了一个shell脚本:

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

使用RAW模式会导致所有文件的大量连接(尽管它还负责将Soy模板甚至CoffeeScript转换为JavaScript)。 不可否认,这使得调试变得很痛苦,因为行号是无稽之谈,但到目前为止我一直运作良好。 Closure Compiler执行的所有检查都使它值得。

闭包编译器的svn HEAD似乎支持AMD

我用一种更简单的方法取代了我的旧方法:

新的方法

  • 没有require()调用我自己的应用程序代码,仅用于Node模块
  • 在运行或编译之前,我需要将服务器代码连接到单个文件
  • 使用简单的grunt脚本完成连接和编译

有趣的是,我甚至不必为require()调用添加extern。 Google Closure编译器自动理解这一点。 我确实必须为我使用的nodejs模块添加externs。

老方法

根据OP的要求,我将详细阐述使用Google Closure Compiler编译node.js代码的方法。

我受到了bolinfest解决问题的方式的启发,我的解决方案使用了同样的原则。 不同之处在于我创建了一个执行所有操作的node.js脚本,包括内联模块(bolinfest的解决方案让GCC负责处理)。 这使它更加自动化,但也更脆弱。

我刚刚为编译服务器代码的每一步添加了代码注释。 看到这个提交: https//github.com/blaise-io/xssnake/commit/da52219567b3941f13b8d94e36f743b0cbef44a3

总结一下:

  1. 我从我的主模块开始,当我想运行它时,我传递给Node的JS文件。
    在我的例子中,这个文件是 start.js
  2. 在此文件中,使用正则表达式,我检测所有 require()调用,包括赋值部分。
    在start.js中,这匹配一个require调用: var Server = require('./lib/server.js');
  3. 我根据文件名检索文件所在的路径,将其内容作为字符串获取,并删除内容中的module.exports赋值。
  4. 然后我将步骤2中的require调用替换为步骤3中的内容。除非它是核心node.js模块,然后我将其添加到我稍后保存的核心模块列表中。
  5. 第3步可能包含更多的 require()调用,所以我递归地重复步骤3和4,直到所有的 require()调用都消失了,我留下了一个包含所有代码的巨大字符串。
  6. 如果所有递归都已完成,我使用REST API编译代码。
    您也可以使用脱机编译器。
    我有每个核心node.js模块的externs。 此工具可用于生成外部
  7. 我预先发布了已删除的core.js模块, require调用已编译的代码。

预编译代码。
所有 require呼叫都被删除。 我的所有代码都被夷为平地。
http://pastebin.com/eC2rVMiN

编译后的代码。
Node.js核心 require手动预先调用。
http://pastebin.com/uB8CaejN


为什么你不应该这样做:

  1. 它使用正则表达式(不是解析器或标记器)来检测 require调用,内联和删除 module.exports 这很脆弱,因为它不包括所有语法变体。
  2. 内联时,所有模块代码都会添加到全局命名空间中。 这违反了Node.js的原则,其中每个文件都有自己的命名空间,如果您有两个具有相同全局变量的不同模块,这将导致错误。
  3. 它不会提高代码的速度,因为V8还执行了大量的代码优化,如内联和死代码删除。

为什么你应该:

  1. 因为它在您具有一致的代码时确实有效。
  2. 启用详细警告时,它将检测服务器代码中的错误。

Node.js上的Closure Library在60秒内完成。

它受支持,请访问https://code.google.com/p/closure-library/wiki/NodeJS

选项4:不要使用闭包编译器。

节点社区中的人不倾向于使用它。 你不需要缩小node.js源代码,这很愚蠢。

对缩小没有好处。

至于关闭的性能优势,我个人怀疑它实际上使您的程序更快。

当然还有成本,调试编译的JavaScript是一场噩梦

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM