Cómo crear una herramienta CLI con Node.js y PhantomJS

 

 

 


Índice
  1. Lo que cubriremos
  • Salsa secreta
  • Instalación de Node.js y npm
  • Process
  • Automation
  • PhantomJS
  • Squirrel
  • Cómo funciona
  • El código
    1. ardilla.js
    2. appcache.js
    3. Volver a ardilla.js
  • embalaje
  • Publicación
  • Conclusión
  • Más sobre Marcos ↬

     

  • Listas de verificación de diseño de interfaz inteligente
  • SmashingConf Nueva York 2024
  • En este artículo, Mark McDonnell repasará las técnicas necesarias para crear una herramienta de línea de comandos utilizando Node.js y PhantomJS (este es sólo un ejemplo del tipo de herramientas de línea de comandos que puede desarrollar con las muchas funciones de Node.js). Considere siempre automatizar el proceso con una herramienta CLI la próxima vez que se encuentre realizando una tarea repetitiva.

    En este artículo, repasaremos los conceptos y técnicas necesarios para crear una herramienta de línea de comandos utilizando Node.js y PhantomJS . Crear una herramienta de línea de comandos le permite automatizar un proceso que de otro modo llevaría mucho más tiempo.

    Las herramientas de línea de comandos están integradas en una gran variedad de lenguajes, pero en el que nos centraremos es en Node.js.

    Lo que cubriremos

    • Salsa secreta
    • Instalación de Node.js y npm
    • Proceso
    • Automatización
    • fantasmajs
    • Ardilla
    • Cómo funciona
    • El código
    • embalaje
    • Publicación
    • Conclusión

    Salsa secreta

    Para aquellos con poco tiempo, he condensado el proceso central en tres pasos. Esta es la salsa secreta para convertir su script Node.js en una herramienta de línea de comandos completamente funcional. Pero quédate para ver qué más tengo para mostrarte.

     

    1. En su package.jsonarchivo, incluya las siguientes configuraciones:
      • "preferGlobal": "true"
      • "bin": { "name-of-command": "path-to-script.js" }
    2. Añadir .#!/usr/bin/env nodepath-to-script.js
    3. Para probar su nuevo comando ( name-of-command), use npm link.

    El resto del proceso consiste simplemente en decidir qué funcionalidad implementar.

    Instalación de Node.js y npm

    Para instalar Node.js, tiene algunas opciones:

    • Instalador específico del sistema operativo para Windows, Mac o binario;
    • Elaboración casera : brew install node;
    • nave ;
    • NVM .

    Tenga en cuenta que npm se instala como parte de Node.js; no hay instalación separada.

    To test that Node.js and npm are installed correctly, run the following commands in your terminal:

    • node --version
    • npm --version

    Process

    Let’s consider a sample process: generating an Application Cache manifest file.

    In case you are unfamiliar with AppCache, it enables you to take your application offline by specifying pages and resources to cache in the event that the user loses their Internet connection or tries to access your application later offline.

    Typically, you would create an appcache.manifest file, where you would configure the offline settings.

    We won’t go into much detail about AppCache itself because that would distract us from the purpose of this article. Nevertheless, below are the lines for a sample file:

    CACHE MANIFESTCACHE:foo.jpgindex.htmloffline.htmlstyles.cssbehaviours.jsNETWORK:*FALLBACK:/ /offline.html

    As you can see, we’ve specified the following:

    • a JPG image,
    • two HTML files,
    • a CSS file,
    • a JavaScript file.

    These are the resources that we want to cache in case the user goes offline.

    We’ve also specified that all other items requested by the user should require a network to be accessed.

    Finally, we’ve stated that any file that should be cached but isn’t yet should redirect the user to a file named offline.html.

    Automation

    Having to manually look up all of the images, style sheets, scripts and other pages linked from a Web page would be tedious. Thus, we’re trying to automate the process of generating an AppCache manifest file.

    We could do this by writing some Node.js code along with some additional tools, but that wouldn’t be very easy (even for the person writing the script), because we would need to open the code and tell it which Web page to interrogate.

    We also want other people to have the benefit of this tool, without their needing to download a folder full of code, change certain lines of code and run commands to run the scripts.

     

    This is why a command line tool would help.

    PhantomJS

    First, we want to figure out how to solve this problem.

    We’ll use a tool named PhantomJS, which is a headless (i.e. chromeless) browser.

    Specifically, it’s a headless WebKit browser, which provides a JavaScript API that we can tap into and that lets us do things such as open Web pages and analyze their network requests. (It does many other things, but those are the two fundamental aspects we’re interested in.)

    We can use a Node.js module to load PhantomJS and interact with its API. We can then convert our code into a command line tool with relative ease using Node.js’s package manager, npm, and a package.json file.

    Squirrel

    Luckily, I’ve already done the work for you. It’s an open-source project named Squirrel.

    To install it, run the command npm install -g squirrel-js.

    Once it’s installed, you can use it by running the command squirrel [url]. For example, squirrel bbc.co.uk/news.

    Esto generaría (en el directorio actual) un appcache.manifestarchivo lleno de todos los recursos de la página relevantes.

    Cómo funciona

    Comencé Squirrel escribiendo primero el código Node.js y PhantomJS relevante para incorporar la funcionalidad que buscaba.

    Luego, agregué un script que inicia ese código y me permite tomar argumentos que configuran cómo se ejecuta el código.

    Terminé con dos guiones:

    • ardilla.js
    • appcache.js

    El primer guión configura la obra:

    • Especificamos el entorno en el que queremos que se ejecute el script (en este caso, Node.js).
    • Analiza los argumentos pasados ​​por el usuario.
    • Leer un appcache.manifestarchivo interno (es decir, ficticio).
    • Abra un proceso secundario de shell, llame a PhantomJS y pásele el script que queremos que ejecute (en este caso, appcache.js) y el archivo de manifiesto ficticio.
    • Cuando el segundo script termine su trabajo (cotejando los datos de la página web), regrese a este primer script y muestre información estadística al usuario y genere el archivo de manifiesto.

    El segundo script procesa la página web que el usuario ha solicitado:

    • Tomamos el archivo de manifiesto ficticio.
    • Cree oyentes para los recursos de la página que se solicitan.
    • Establezca el tamaño de la ventana gráfica.
    • Abra la página web y almacene los recursos.
    • Obtenga todos los enlaces de la página (ejecutando código JavaScript directamente en la página web).
    • Convierta el contenido del archivo de manifiesto e inyecte los recursos encontrados y luego devuélvalo como un archivo JSON.

    El código

    Ahora que comprende lo que hace el código, repasémoslo. Mostraré el código completo y luego lo revisaremos poco a poco.

    ardilla.js

    #!/usr/bin/env nodevar 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);}

    La primera línea, #!/usr/bin/env nodees fundamental para el script que se utiliza en el shell. Tenemos que decirle al shell qué proceso debe manejar el script.

     

    A continuación, tenemos que recuperar los argumentos pasados ​​al comando. Si ejecutamos squirrel bbc.co.uk/news, entonces process.argvsería una matriz que contendría lo siguiente:

    • el tipo de ejecución del script ( node);
    • el script que se está ejecutando ( squirrel.js);
    • cualquier otro argumento (en este caso, solo uno, bbc.co.uk/news).

    Ignore los dos primeros argumentos y almacene los argumentos específicos del usuario para que podamos hacer referencia a ellos más adelante:

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

    Nuestro script solo sabe cómo manejar un único argumento (que es la URL de la página a cargar). La siguiente línea no es realmente necesaria porque ignoraremos más de un argumento, pero es útil que el código tenga una intención clara, por lo que arrojaremos un error si se pasa más de un argumento.

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

    Como estamos usando PhantomJS, necesitaremos abrir un shell y llamar al phantomjscomando:

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

    También necesitaremos hacer referencia al bindirectorio donde está almacenado el ejecutable PhantomJS:

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

    A continuación, almacene una referencia al script que queremos que ejecute PhantomJS, así como al archivo de manifiesto ficticio.

     

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

    Debido a que el script PhantomJS que ejecutaremos necesita una referencia al archivo de manifiesto ficticio, leeremos de forma asincrónica el contenido del archivo y luego lo pasaremos a una bootstrapfunción:

    fs.readFile(manifest, bootstrap);

    Nuestra bootstrapfunción hace exactamente lo que usted esperaría: iniciar nuestra aplicación (en este caso, abriendo el shell y llamando a PhantomJS). También notarás que Node.js pasa el contenido del manifiesto como un búfer, que debemos convertir nuevamente en una cadena: Muestras gratis y regalos

    function bootstrap(err, contentAsBuffer) { if (err) throw err; manifestContent = contentAsBuffer.toString('utf8'); shell(phantomjs, [scriptToExecute, url, manifestContent], function(err, stdout, stderr) { // code... });}

    En este punto de la ejecución del código, estamos en el appcache.jsarchivo. Vayamos hacia allí ahora.

    appcache.js

    El propósito de appcache.jses obtener información de la página solicitada por el usuario y devolverla squirrel.jspara su procesamiento.

    Nuevamente, mostraré el guión en su totalidad y luego lo desglosaremos. (No se preocupe, no repasaremos cada línea, solo las partes importantes).

    var unique = require('lodash.uniq');var system = require('system');var fs = require('fs');var page = require('webpage').create();var args = system.args;var manifest = args[2];var css = [];var images = [];var javascript = [];var links;var url;var path;bootstrap();pageSetUp();openPage();function bootstrap() { if (urlProvided()) { url = cleanUrl(args[1]); } else { var error = new Error('Sorry, a valid URL could not be recognized'); error.additional = 'Valid URL example: bbc.co.uk/news'; throw error; phantom.exit(); } if (bbcNews()) { // We want to serve the responsive code base. phantom.addCookie({ 'name' : 'ckps_d', 'value' : 'm', 'domain': '.bbc.co.uk' }); }}function pageSetUp() { page.onResourceRequested = function(request) { if (/.(?:png|jpeg|jpg|gif)$/i.test(request.url)) { images.push(request.url); } if (/.(?:js)$/i.test(request.url)) { javascript.push(request.url); } if (/.(?:css)$/i.test(request.url)) { css.push(request.url); } }; page.onError = function(msg, trace) { console.log('Error :', msg); trace.forEach(function(item) { console.log('Trace: ', item.file, ':', item.line); }); } page.viewportSize = { width: 1920, height: 800 };}function openPage() { page.open(url, function(status) { links = unique(getLinks()); images = unique(images); css = unique(css); javascript = unique(javascript); populateManifest(); // Anything written to stdout is actually passed back to our Node script callback console.log(JSON.stringify({ links : links.length, images : images.length, css : css.length, javascript : javascript.length, manifestContent : manifest })); phantom.exit(); });}function urlProvided() { return args.length 1 /(?:www.)?[a-z-z1-9]+./i.test(args[1]);}function cleanUrl(providedUrl) { // If no http or https found at the start of the URL... if (/^(?!https?://)[wd]/i.test(providedUrl)) { return 'https://' + providedUrl + '/'; }}function bbcNews(){ if (/bbc.co.uk/news/i.test(url)) { return true; }}function getLinks() { var results = page.evaluate(function() { return Array.prototype.slice.call(document.getElementsByTagName('a')).map(function(item) { return item.href; }); }); return results;}function writeVersion() { manifest = manifest.replace(/# Timestamp: d+/i, '# Timestamp: ' + (new Date()).getTime());}function writeListContentFor(str, type) { manifest = manifest.replace(new RegExp('(# ' + str + ')n[sS]+?nn', 'igm'), function(match, cg) { return cg + 'n' + type.join('n') + 'nn'; });}function populateManifest() { writeVersion(); writeListContentFor('Images', images); writeListContentFor('Internal HTML documents', links); writeListContentFor('Style Sheets', css); writeListContentFor('JavaScript', javascript);}

    Comenzamos usando la API de PhantomJS para crear una nueva página web:

     

    var page = require('webpage').create();

    A continuación, comprobaremos que se proporcionó una URL y, de ser así, la limpiaremos en el formato requerido (por ejemplo, dándole un httpprotocolo). De lo contrario, arrojaremos un error y detendremos PhantomJS:

    if (urlProvided()) { url = cleanUrl(args[1]);} else { var error = new Error('Sorry, a valid URL could not be recognized'); error.additional = 'Valid URL example: bbc.co.uk/news'; throw error; phantom.exit();}

    También verificamos si la URL pasada era para bbc.co.uk/newsy, de ser así, usamos PhantomJS para configurar una cookie que permite que se cargue la versión responsiva del sitio web (el propósito es simplemente demostrar algunas de las API útiles de PhantomJS, como como addCookie):

    if (bbcNews()) { phantom.addCookie({ 'name' : 'ckps_d', 'value' : 'm', 'domain': '.bbc.co.uk' });}

    Para que PhantomJS pueda analizar los datos de la red (para que podamos rastrear las hojas de estilo, JavaScript y las imágenes solicitadas por la página), necesitamos usar controladores especiales de PhantomJS para interpretar las solicitudes:

    page.onResourceRequested = function(request) { if (/.(?:png|jpeg|jpg|gif)$/i.test(request.url)) { images.push(request.url); } if (/.(?:js)$/i.test(request.url)) { javascript.push(request.url); } if (/.(?:css)$/i.test(request.url)) { css.push(request.url); }};

    También usaremos otra característica de la API de PhantomJS que nos permite determinar el tamaño de la ventana del navegador:

    page.viewportSize = { width: 1920, height: 800 };

    Luego le decimos a PhantomJS que abra la página web especificada. Una vez que la página está abierta (es decir, el loadevento se ha activado), se ejecuta una devolución de llamada:

     

    page.open(url, function(status) { // code...});

    En la devolución de llamada, almacenamos los recursos que se encontraron y llamamos a una función que reemplaza el contenido de nuestra cadena (el manifiesto ficticio) con una lista de cada conjunto de recursos:

    page.open(url, function(status) { links = unique(getLinks()); images = unique(images); css = unique(css); javascript = unique(javascript); populateManifest(); // Remaining code...});

    Finalmente, creamos un objeto de datos para contener estadísticas sobre los recursos que se solicitan, lo convertimos en una cadena JSON y lo registramos mediante la consoleAPI.

    Una vez hecho esto, le decimos a PhantomJS que exit(de lo contrario, el proceso se detendría):

    page.open(url, function(status) { // Previous code... console.log(JSON.stringify({ links : links.length, images : images.length, css : css.length, javascript : javascript.length, manifestContent : manifest })); phantom.exit();});

    Al revisar el código anterior, quizás se pregunte cómo recuperamos los datos en nuestro squirrel.jsscript. Échale otro vistazo al console.log. El código tiene un efecto secundario extraño, que es que cualquier código registrado por PhantomJS se devuelve a nuestra devolución de llamada de shell (ejecutado originalmente en squirrel.js).

    Repasemos nuestro squirrel.jsguión ahora.

    Volver a ardilla.js

    shell(phantomjs, [scriptToExecute, url, manifestContent], function(err, stdout, stderr) { if (err) throw err; 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();});

    La función de devolución de llamada se ejecuta cuando el script PhantomJS termina de ejecutarse. Se pasa los errores que se hayan podido producir y, si los hay, arrojamos el error:

    if (err) throw err;

    Los otros argumentos son los argumentos de error y de salida estándar proporcionados por el shell. En este caso, la salida estándar sería nuestra cadena JSON, de la que console.logextrajimos appcache.js. Analizamos la cadena JSON y la volvemos a convertir en un objeto para poder presentar los datos al usuario que ejecutó el squirrelcomando.

    Como nota al margen, envolvemos esta conversión en una try/catchcláusula para proteger contra páginas web que provocan un error de JavaScript (el error lo detecta stdout, no stderr, lo que provoca que se interrumpa el análisis JSON):

    try { data = JSON.parse(stdout);} catch(err) { error(err);}

    Una vez que tenemos nuestros datos, llamamos a displayStatistics, que utiliza stdoutpara escribir un mensaje en el terminal del usuario.

    Por último, llamamos a createManifestFile, que crea un appcache.manifestarchivo en el directorio actual del usuario:

    fs.writeFile(process.cwd() + '/appcache.manifest', data.manifestContent, function(err) { if (err) throw err; log('nManifest file created');});

    Ahora que entendemos cómo funciona el script en su totalidad, veamos cómo permitir que otros descarguen e instalen nuestro trabajo.

     

    embalaje

    Para que otros usuarios puedan instalar nuestro módulo, necesitaremos publicarlo en un repositorio público. El lugar para hacer esto es el registro npm .

    Para publicar en npm, necesitará un package.jsonarchivo.

    El propósito de package.jsones especificar las dependencias del proyecto en el que estás trabajando. En este caso, especifica las dependencias requeridas por Squirrel para realizar su trabajo.

    A continuación se muestra el archivo de Squirrel package.json:

    { "name": "squirrel-js", "version": "0.1.3", "description": "Node.js-based CLI tool, using PhantomJS to automatically generate an Application Cache manifest file for a specified URL", "main": "lib/squirrel", "scripts": { "test": "echo "Error: no test specified" exit 1" }, "engines": { "node": "=0.10" }, "repository": { "type": "git", "url": "git://github.com/Integralist/Squirrel.git" }, "preferGlobal": "true", "bin": { "squirrel": "lib/squirrel.js" }, "dependencies": { "phantomjs": "~1.9.2-6", "lodash.uniq": "~2.4.1" }, "keywords": [ "appcache", "phantomjs", "cli" ], "author": "Mark McDonnell [email protected] (https://www.integralist.co.uk/)", "license": "MIT", "bugs": { "url": "https://github.com/Integralist/Squirrel/issues" }, "homepage": "https://github.com/Integralist/Squirrel"}

    Puede leer sobre todas las propiedades package.jsonen el registro npm.

    Las propiedades a destacar son estas:

    • "preferGlobal": "true"
    • "bin": { "squirrel": "lib/squirrel.js" }

    La primera propiedad indica cuándo un usuario ha instalado un módulo que preferiría que se instalara globalmente. En este caso, queremos que se instale globalmente porque así el usuario podrá ejecutar el comando en cualquier parte de su sistema.

    La segunda propiedad indica dónde el comando encontrará el código requerido para ejecutar el comando.

    Para probar que su comando funciona, deberá ejecutar el npm linkcomando, que en este caso crea un enlace simbólico del squirrelcomando al squirrel.jsarchivo.

    Publicación

    Para publicar su código, primero regístrese para obtener una cuenta npm.

    Deberá verificar la cuenta a través de la línea de comando. Para hacer esto, ejecute npm adduser, que le pedirá que especifique un nombre de usuario y contraseña.

    Una vez que haya verificado la cuenta, puede publicar su módulo en el registro npm usando npm publish.

    El módulo podría tardar unos minutos en volverse accesible públicamente.

    Tenga en cuenta que si actualiza el código e intenta ejecutarlo npm publishsin actualizar la propiedad package.jsondel archivo version, npm devolverá un error pidiéndole que actualice el número de versión.

    Conclusión

    Este es sólo un ejemplo del tipo de herramientas de línea de comandos que puedes desarrollar con las muchas características de Node.js.

    La próxima vez que se encuentre realizando una tarea repetitiva, considere automatizar el proceso con una herramienta CLI.

    ###Otras lecturas

    • Herramientas, tutoriales y recursos útiles de Node.js
    • Una introducción detallada a Webpack
    • Navegando con Sails.js: un marco estilo MVC para Node.js
    • El problema con los paquetes de nodos globales

    (al, señor)Explora más en

    • Codificación
    • javascript
    • Tutoriales
    • Técnicas
    • Recursos
    • Nodo.js





    Tal vez te puede interesar:

    1. 50 herramientas útiles de JavaScript
    2. 50 nuevas herramientas de JavaScript que mejorarán su flujo de trabajo
    3. Herramientas, bibliotecas y complementos útiles de JavaScript y jQuery
    4. Herramientas útiles de HTML, CSS y JavaScript que hacen prácticamente de todo

    Cómo crear una herramienta CLI con Node.js y PhantomJS

    Cómo crear una herramienta CLI con Node.js y PhantomJS

    Índice Lo que cubriremos Salsa secre

    programar

    es

    https://pseint.es/static/images/programar-como-crear-una-herramienta-cli-con-node-838-0.jpg

    2024-04-04

     

    Cómo crear una herramienta CLI con Node.js y PhantomJS
    Cómo crear una herramienta CLI con Node.js y PhantomJS

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20