Как создать CLI инструмент с помощью Node.js и PhantomJS.

В этой статье мы рассмотрим понятия и методы, которые необходимо знать для создания инструмента на основе командной строки. Для этого мы будем использовать такие популярные программные платформы, как Node.js и PhantomJS. Создание инструмента на основе командной строки позволяет автоматизировать процессы, на выполнение которых в противном случае тратилось бы слишком много времени.

Инструменты на основе командной строки можно создать с помощью множества языков, но в данном случае мы сосредоточимся на программной платформе Node.js.

Что мы будем рассматривать.

  • Секретный совет.
  • Установка Node.js и npm.
  • Процесс.
  • Автоматизация.
  • PhantomJS.
  • Squirrel (белка).
  • Принцип работы.
  • Код.
  • Упаковка.
  • Публикация.
  • Заключение.

Секретный совет.

Для тех из вас, у кого катастрофически не хватает времени, я разбил весь процесс на три основных этапа. Это секретный совет по преобразованию Node.js скрипта в полнофункциональный инструмент на основе командной строки. Внимательно следите за всем, что я показываю в этой статье.

  1. В вашем package.json файле используйте следующие параметры:
    • "preferGlobal": "true"
    • "bin": { "name-of-command": "path-to-script.js" }
  2. Добавьте #!/usr/bin/env node в path-to-script.js.
  3. Чтобы проверить новую команду (name-of-command), используйте npm link.

Остальная часть процесса будет заключаться в выборе и реализации функциональных возможностей.

Установка Node.js И npm.

Для установки Node.js можно воспользоваться несколькими вариантами:

Обратите внимание, что npm (New public management - пакетный менеджер) устанавливается как часть Node.js; отдельной инсталляции не существует.

Чтобы проверить правильность установки Node.js и npm, в терминале выполните следующие команды:

  • node --version
  • npm --version

Процесс.

Давайте рассмотрим типовой процесс: создание манифест файла кэша приложения .

В случае, если вы не знакомы с AppCache, то это оффлайн хранилище для приложений. Вы можете обеспечить функционирование приложения даже если пользователь теряет подключение к Интернету или пытается получить доступ к приложению в режиме оффлайн. Вам всего лишь нужно указать страницы и ресурсы, для которых необходимо выполнить кэширование.

Как правило, создается appcache.manifest файл, в котором хранится конфигурация оффлайн настроек.

Мы не будем вдаваться в подробности функционирования AppCache, поскольку это выходит за рамки нашей темы и отвлекает нас от целей настоящей статьи. Тем не менее, ниже представлены строки из типового файла:

CACHE MANIFEST

CACHE:
foo.jpg
index.html
offline.html
styles.css
behaviours.js

NETWORK:
*

FALLBACK:
/ /offline.html

Как вы уже наверное заметили, мы определили следующие объекты:

  • JPG изображение,
  • два HTML файла,
  • CSS файл,
  • JavaScript файл.

Это перечень тех ресурсов, которые необходимо кэшировать на тот случай, если у пользователя пропадет подключение к интернету.

Мы также определили все элементы, при запросе которых пользователем, должно должно присутствовать подключение к интернету.

Наконец, мы определились с тем, что любой файл, который должен быть кэширован и не перенаправляется пользователю, будет иметь название offline.html.

Автоматизация.

Процесс последовательного просмотра всех изображений, таблиц стилей, скриптов и ссылок на другие веб-страницы может быть крайне утомительным. По этой причине мы попытаемся автоматизировать процесс генерации манифест файла.

Мы могли бы сделать это, написав специальный Node.js код и воспользовавшись дополнительными инструментами. Однако данная задача будет не такой уж и простой (даже для человека, который имеет огромный опыт создания скриптов), поскольку мы должны будем открыть код и указать, какую именно веб-страницу необходимо опросить.

Мы также хотим, чтобы и другие люди могли воспользоваться преимуществами этого инструмента, без необходимости загружать папку с кодом, вносить корректировки и инициировать команды запуска скриптов.

Вот почему особенно полезным будет инструмент на основе командной строки.

PhantomJS.

Во-первых, мы хотим выяснить, как можно решить эту проблему.

Мы будем использовать инструмент под названием PhantomJS, который представляет собой headless (безголовый) (т.е. Chromeless) браузер.

В частности, это headless WebKit браузер, который предоставляет интерфейс прикладного программирования (API) для JavaScript. С помощью этого интерфейса мы можем подключиться к необходимым нам ресурсам, что позволяет выполнять такие действия, как открытие веб-страниц и анализирование их сетевых запросов (он также имеет множество других функциональных возможностей, но в данном случае мы заинтересованы только в этих двух фундаментальных аспектах).

Мы можем использовать модуль Node.js для загрузки PhantomJS и налаживания взаимодействия с его API. С помощью Node.js менеджера пакетов, npm и package.json файла мы с относительной легкостью можем преобразовать наш код в инструмент на основе командной строки.

Squirrel.

К счастью, я уже сделал за вас всю работу. Это проект с открытым исходным кодом под названием Squirrel (англ. «белка» — интерпретируемый язык программирования, разработанный специально для использования в качестве скриптового языка в приложениях реального времени, таких как компьютерные игры).

Чтобы установить его используйте команду npm install -g squirrel-js.

После установки этого проекта вы можете приступить к его использованию просто запустив команду squirrel [url]. Например, squirrel bbc.co.uk/news.

Будет создан (в текущем каталоге) файл с названием appcache.manifest, заполненный всеми соответствующими ресурсами страницы.

Принцип работы?

Я начал проект Squirrel с написания релевантного Node.js и PhantomJS кода, который позволил бы реализовать необходимые функциональные возможности.

Затем я добавил скрипт, который позволяет вносить изменения в этот код и использовать аргументы, предназначенные для настройки выполнения кода.

Результатом стало создание двух скриптов:

Первый скрипт отвечает за рабочие настройки:

  • Мы задаем программную среду, в которой должен будет выполняться скрипт (в данном случае, Node.js).
  • Анализ аргументов, передаваемых пользователем.
  • Чтение внутреннего (т.е. фиктивного) appcache.manifest файла.
  • Открытие дочернего процесса, вызов PhantomJS и передача ему скрипта, который мы хотим выполнить (в данном случае, appcache.js), а также фиктивного манифест файла.
  • Когда второй скрипт завершает работу (сбор данных на веб-странице), происходит возврат к первому скрипту и отображение статистической информации для пользователя и генерация манифест файла.

Второй скрипт выполняет обработку веб-страницы, запрашиваемой пользователем:

  • Берет фиктивный манифест файл.
  • Создает слушателей для требуемых страничных ресурсов.
  • Устанавливает размер окна просмотра.
  • Открывает веб-страницу и сохраняет ресурсы.
  • Получает все ссылки со страницы (путем выполнения JavaScript кода непосредственно на веб-странице).
  • Преобразует содержимое манифест файла и вводит найденные ресурсы, а затем возвращает его как JSON файл.

Код.

Теперь, когда вы имеете представление о выполняемых действиях, давайте приступим к непосредственному рассмотрению кода. Сначала я приведу весь код целиком, а затем мы детально рассмотрим отдельные его фрагменты.

Squirrel.js

#!/usr/bin/env node

var userArguments = process.argv.slice(2); // Copies arguments list but removes first two options (script exec type & exec location)

if (userArguments.length > 1) {
    throw new Error('Only one argument may be specified (the URL for which you want to generate the AppCache.)');
}

var fs               = require('fs');
var shell            = require('child_process').execFile;
var phantomjs        = require('phantomjs').path;
var scriptToExecute  = __dirname + '/appcache.js';
var manifest         = __dirname + '/../appcache.manifest';
var url              = userArguments[0];
var manifestContent;
var data;

fs.readFile(manifest, bootstrap);

function bootstrap(err, contentAsBuffer) {
    if (err) throw err;

    manifestContent = contentAsBuffer.toString('utf8');

    shell(phantomjs, [scriptToExecute, url, manifestContent], function(err, stdout, stderr) {
        if (err) throw err;

        // Sometimes an error in the loaded page's JavaScript doesn't get picked up or thrown,
        // but the error comes in via stdout and causes JSON parsing to break
        try {
            data = JSON.parse(stdout);
        } catch(err) {
            log('Whoops! It seems there was an error? You\'ll find the stack trace below.');
            error(err);
        }

        displayStatistics();
        createManifestFile();
    });
}

function displayStatistics() {
    log(''); // Adds extra line of spacing when displaying the results
    log('Links: '      + data.links);
    log('Images: '     + data.images);
    log('CSS: '        + data.css);
    log('JavaScript: ' + data.javascript);
}

function createManifestFile() {
    fs.writeFile(process.cwd() + '/appcache.manifest', data.manifestContent, function(err) {
        if (err) throw err;

        log('\nManifest file created');
    });
}

function log(message) {
    process.stdout.write(message + '\n');
}

function error(err) {
    process.stderr.write(err);
}

Первая строка, #!/usr/bin/env node, имеет решающее значение для скриптов, используемых в оболочке. Мы должны сообщить оболочке, какой именно процесс должен обрабатывать скрипт.

Далее мы должны получить аргументы, передаваемые командой. Если мы запустим squirrel bbc.co.uk/news, то process.argv будет представлять собой массив, содержащий следующую информацию:

  • тип исполнительного скрипта (node);
  • выполняемый скрипт (squirrel.js);
  • любые другие аргументы (в данном случае он будет только один, bbc.co.uk/news).

Не обращайте внимания на первые два аргумента, и сохраняйте аргументы пользовательских настроек, чтобы мы могли ссылаться на них позднее:

var userArguments = process.argv.slice(2);

Наш скрипт может обработать только один аргумент (который представляет собой URL-адрес страницы для загрузки). Следующая строка на самом деле не нужна, поскольку мы будем игнорировать любые аргументы, кроме первого. Но все же она представляет для кода определенную пользу, потому что в случае использования более одного аргумента будет выведено соответствующее сообщение об ошибке.

if (userArguments.length > 1) {
    throw new Error('Only one argument may be specified (the URL for which you want to generate the AppCache.)');
}

Поскольку мы используем PhantomJS, то должны будем открыть оболочку и вызвать команду phantomjs:

var shell = require('child_process').execFile;

Мы также должны ссылаться на bin каталог, где хранится исполняемый PhantomJS код:

var phantomjs = require('phantomjs').path;

Следующим шагом является сохранение ссылки на скрипт, который должен выполняться PhantomJS, также как и на фиктивной манифест файл.

var scriptToExecute = __dirname + '/appcache.js';
var manifest        = __dirname + '/../appcache.manifest';
var url             = userArguments[0];

Поскольку PhantomJS скрипт, который мы будем выполнять, предполагает наличие ссылки на фиктивный манифест файл, то мы будем асинхронно считывать содерж …

Если вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.