Я использовал Closure Compiler с Node для проекта, который я еще не выпустил. Это заняло немного времени, но помогло поймать много ошибок и имеет довольно короткий цикл edit-restart-test.
Во-первых, я использую plovr (это проект, который я создал и поддерживаю), чтобы совместно использовать Closure Compiler, Library и Templates. Я пишу свой код Node в стиле библиотеки Closure, поэтому каждый файл определяет свой собственный класс или набор утилит (например, goog.array
).
Следующим шагом является создание группы внешних файлов для функций Node, которые вы хотите использовать. Я опубликовал некоторые из них публично по адресу:
https://github.com/bolinfest/node-google-closure-latitude-experiment/tree/master/externs/node/v0.4.8
Хотя, в конечном счете, я думаю, что это должно быть в большей степени обусловлено интересами сообщества, поскольку существует множество функций для документирования. (Это также раздражает, потому что некоторые функции Node имеют необязательные средние аргументы, а не последние аргументы, что усложняет аннотации типов.) Я сам не начал это движение, потому что вполне возможно, что мы могли бы поработать с Closure Complier, чтобы сделать это менее неудобным (см. ниже).
Допустим, вы создали файл externs для пространства имен Node http
. В моей системе я решил, что когда мне понадобится http
, я включу его через:
var http = require('http');
Хотя я не включаю этот require()
вызов в мой код. Вместо этого я использую функцию output-wrapper
в Closure Compiler для добавления всех require()
s в начале файла, что при объявлении в 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's require()
, но компилятор допускает использование таких вещей, как http
в моем коде, потому что компилятор распознает их как внешние. Поскольку они не являются истинными внешними, их необходимо добавлять в начале, как я описал.
В конечном итоге, после разговора об этом в списке обсуждений , я думаю, что лучшим решением будет использование аннотации нового типа для пространств имен, которая будет выглядеть примерно так:
goog.scope(function() {
/** @type {~NodeHttpNamesapce} */
var http = require('http');
// Use http throughout.
});
В этом сценарии файл externs будет определять NodeHttpNamespace
таким образом, чтобы компилятор Closure смог проверить его свойства с помощью файла externs. Разница здесь в том, что вы можете назвать возвращаемое значение require()
как хотите, потому что тип http
будет этим специальным типом пространства имен. (Идентификация "пространства имен jQuery" для $
является аналогичной проблемой.) Этот подход устраняет необходимость согласованного именования локальных переменных для пространств имен Node и устраняет необходимость в этом гиганте output-wrapper
в конфигурации plovr.
Но это было отступление ... как только у меня все настроено, как описано выше, у меня есть скрипт оболочки, который:
- Использует plovr для сборки всего в режиме
RAW
.
- Запускает
node
для файла, сгенерированного plovr.
Использование режима RAW
приводит к большой конкатенации всех файлов (хотя он также заботится о переводе шаблонов сои и даже CoffeeScript в JavaScript). По общему признанию, это делает отладку болезненной, потому что номера строк бессмысленны, но до сих пор работали достаточно хорошо для меня. Все проверки, выполненные компилятором Closure, оправдывают себя.