Цель этого туториала — дать вам хорошее представление о необходимых шагах и доступных инструментах для миграции существующего нативного аддон-модуля Node на NAN в Node-API с помощью пакета node-addon-api.
Этот туториал использует инструмент конвертации, поставляемый с Node-API, чтобы дать вам фору в миграции. Однако инструмент конвертации доведёт вас лишь до определённой точки. Дополнительная ручная доработка всё же потребуется, как описано ниже.
Чтобы держать всё в некоторых рамках, этот туториал использует node-microtime — простой нативный аддон на основе NAN. Этот аддон делает системные вызовы, чтобы определить текущее время с точностью до микросекунд, если это поддерживается операционной системой.
Прежде чем начать, убедитесь, что у вас установлены все необходимые пре-реквизиты и инструменты.
Node-API стабилен во всех поддерживаемых в настоящее время релизах Node.js. Используйте релиз Active LTS или Maintenance LTS. Проверить, какую версию Node.js вы используете, можно командой
node -v.
Первым шагом склонируйте GitHub-репозиторий node-microtime на вашу систему:
git clone https://github.com/wadey/node-microtime.gitПрежде чем вносить наши изменения, хорошая идея — сначала собрать и протестировать node-microtime, чтобы убедиться, что необходимые инструменты разработки корректно установлены и настроены.
Поскольку
node-microtimeуже мигрировал наnode-addon-api, вам нужно переключиться на тегv2.1.9, чтобы следовать этому туториалу.
cd node-microtime
git checkout tags/v2.1.9
npm install
npm testКоманда npm install запускает процесс сборки, а npm test выполняет код. Вы можете увидеть предупреждения компилятора, которые не влияют на возможность запуска кода. При успешной сборке и запуске вы должны увидеть вывод примерно такой:
microtime.now() = 1526334357974754
microtime.nowDouble() = 1526334357.976626
microtime.nowStruct() = [ 1526334357, 976748 ]
Guessing clock resolution...
Clock resolution observed: 1us
Как только базовая работа кода проверена, следующий шаг — запустить инструмент конвертации Node-API. Учтите, что инструмент конвертации заменяет файлы на месте. Никогда не запускайте инструмент конвертации на единственной копии вашего проекта. И, очевидно, вы хотите запустить его только один раз.
npm install --save node-addon-api
node ./node_modules/node-addon-api/tools/conversion.js ./Для этого небольшого проекта инструмент конвертации отрабатывает очень быстро. К этому моменту инструмент конвертации изменил следующие файлы проекта:
binding.gyppackage.jsonsrc/microtime.cc
Пересоберите сконвертированный код:
npm installКак вы увидите, есть одна или несколько ошибок компиляции, которые нужно устранить. Порой их может быть довольно много, но ничего непреодолимого.
Инструмент конвертации не может предусмотреть каждую ситуацию в коде. Так что обычно будут проблемы, которые нужно решить вручную. Ниже — проблемы, с которыми вы, скорее всего, столкнётесь в этом проекте. Лучший подход — решать каждую проблему по одной и пытаться выполнить npm install после устранения каждой, пока ошибок не останется.
Эта ошибка, а также её аналог, где не удаётся найти napi.h, вызвана отсутствием кода в файле binding.gyp. Для этого проекта вы увидите такой код в binding.gyp:
'include_dirs' : [ '<!(node -e "require(\'nan\')")' ]Директории include для C/C++ всё ещё указывают на NAN. Вместо этого они должны указывать на Node-API. Строку выше следует заменить на такую:
'include_dirs' : [ "<!@(node -p \"require('node-addon-api').include\")" ]В других проектах вы можете получить ошибку, где не удаётся найти napi.h. Причина та же. Свойство include_dirs каждого target'а должно включать ссылку на node-addon-api, как показано выше.
Три C++-функции — Now, NowDouble и NowStruct — каждая ссылается на переменную env, которая не определена. env призвана хранить переменную окружения Node-API, которая существенна почти для всех вызовов Node-API. Значение для env легко получить из аргумента Napi::CallbackInfo, передаваемого каждой C++-функции. Один из способов исправить эту ошибку — добавить следующий код первой строкой в тело каждой функции:
Napi::Env env = info.Env();Альтернативой было бы заменить каждое вхождение env в трёх функциях на info.Env(). Выбор за вами.
Каждая из трёх C++-функций — Now, NowDouble и NowStruct — определена как возвращающая значение void. На самом деле каждая из них должна возвращать значение JavaScript. Лучший способ этого добиться — заменить void на Napi::Value. Это позволяет каждой из функций возвращать значение JavaScript неопределённого типа. Это может быть любое значение JavaScript, включая String, Number, Boolean, Array и т. д. Вот как они должны выглядеть:
Napi::Value Now(const Napi::CallbackInfo&info) {
Napi::Value NowDouble(const Napi::CallbackInfo&info) {
Napi::Value NowStruct(const Napi::CallbackInfo&info) {Node-API использует другую технику определения объекта exports.
Код:
Nan::Export(target, "now", Now);
Nan::Export(target, "nowDouble", NowDouble);
Nan::Export(target, "nowStruct", NowStruct);Нужно заменить на:
exports.Set(Napi::String::New(env,"now"), Napi::Function::New(env, Now));
exports.Set(Napi::String::New(env,"nowDouble"), Napi::Function::New(env, NowDouble));
exports.Set(Napi::String::New(env,"nowStruct"), Napi::Function::New(env, NowStruct));exports — это Napi::Object, представляющий JavaScript-объект. Метод Set устанавливает значение свойств объекта и принимает два аргумента: имя свойства и его значение. Оба этих аргумента должны быть значениями JavaScript.
Ещё одно изменение критично для работы Node-API. Функция InitAll обязана возвращать переменную exports. Эта строка должна быть последней в теле функции:
return exports;Если не добавить эту строку, это, скорее всего, приведёт к ошибке segfault во время выполнения.
Объекта ErrnoException из NAN в Node-API не существует. Существующий код выглядит так:
Napi::Error::New(env, Napi::ErrnoException(errno, "gettimeofday")).ThrowAsJavaScriptException();Но его легко заменить кодом, который выглядит так:
std::string msg = "gettimeofday: " + std::string(strerror(errno));
Napi::Error::New(env, msg).ThrowAsJavaScriptException();Как только код компилируется без ошибок, протестируйте внесённые вами изменения:
npm testВы должны увидеть результаты, похожие на те, что были до миграции.
Поздравляем! Вы только что сконвертировали свой первый NAN-модуль в Node-API.
Признаться, этот туториал лишь слегка касается миграции NAN-модулей в Node-API. Однако базовый подход тот же. Запустите конвертацию, попробуйте скомпилировать, устраните ошибки, скомпилируйте снова. Намыливаем. Смываем. Повторяем.