NodeJS

How to emulate keyboard input in Puppeteer

To emulate the user typing something on the keyboard, use

await page.keyboard.type("the text");

This will type the text extremely fast with virtually no delay between the characters.

In order to simulate the finite typing speed of real users, use

await page.keyboard.type("the text", {delay: 100});

instead. The delay betwen characters in this example is 100 Milliseconds, i.e. the emulated user types 10 characters per second.

Posted by Uli Köhler in Javascript, NodeJS, Puppeteer

How to save screenshot in Puppeteer as PNG?

You can take a screenshot in Puppeteer using

await page.screenshot({path: 'screenshot.png'});

The path is relative to the current working directory.

Want to have a screenshot in a size different to 800×600? See How to set screenshot size in Puppeteer?

Full example:

// Minimal puppeteer example
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Screenshot to verify result
  await page.screenshot({path: 'screenshot.png'});
  // Cleanup
  await browser.close();
})();

 

Posted by Uli Köhler in Javascript, NodeJS, Puppeteer

How to sleep in NodeJS using async/await

The easiest way to sleep using async/await in NodeJS is the sleep-promise package:

npm install --save sleep-promise

Using that package you can simply use await sleep(milliseconds) syntax like this:

const sleep = require('sleep-promise');

// In any async function:
await sleep(2000); // Wait 2000 ms

Note that this sleep() variant is fully asynchronous, IO and other asynchronous operations will still be able to continue in the background – sleep() will not block the NodeJS process.

Full example:

const sleep = require('sleep-promise');

(async () => {
    console.log("This prints immediately");
    await sleep(2000);
    console.log("This prints 2 seconds later");
})();

 

Posted by Uli Köhler in Javascript, NodeJS

NodeJS equivalent to Python’s time.sleep() using async/await

Install the sleep-promise package:

npm install --save sleep-promise

then you can use

const sleep = require('sleep-promise');

// In any async function:
await sleep(2000); // Wait 2000 ms

Full example:

const sleep = require('sleep-promise');

(async () => {
    console.log("This prints immediately");
    await sleep(2000);
    console.log("This prints 2 seconds later");
})();

 

Posted by Uli Köhler in Javascript, NodeJS

Puppeteer minimal example

You can use this example as a starting point for your puppeteer application.

// Minimal puppeteer example
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  //
  // YOUR CODE GOES HERE!
  //
  await browser.close();
})();

The screenshot is 800x600px by default and could look like this:

Posted by Uli Köhler in Javascript, NodeJS, Puppeteer

How to install NodeJS 12.x on Ubuntu in 1 minute

Run these shell commands on your Ubuntu computer to install NodeJS 12.x:

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs

Instead of setup_12.x you can also choose other versions like setup_8.x. However, using this method, you can’t install multiple versions of NodeJS in parallel.

Source: Official nodesource documentation

Posted by Uli Köhler in Linux, NodeJS

How to fix npm ERR! missing script: start

Problem:

You want to run a NodeJS app using

npm start

but you only see this error message:

npm ERR! missing script: start

Solution:

You need to tell npm what to do when you run npm start explicitly by editing package.json.

First, identify the main file of your application. Most often it is called index.js, server.js or app.js. When you open package.json in an editor, you can also often find a line like

"main": "index.js",

In this example, index.js is the main file of the application.

Now we can edit package.json to add a start script.

In package.json, find the "scripts" section. If you have a default package.json, it will look like this:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
},

Now add a comma to the end of the "test" line and add this line after it:

"start": "node index.js"

and replace index.js by the main file of your application (e.g. app.js, server.js, index.js etc).

The "scripts" section should now look like this:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "start": "node index.js"
},

Now save and close package.json and run

npm start

to start your application.

Posted by Uli Köhler in Javascript, NodeJS

MongoDB: How to run db.adminCommand() in NodeJS

Problem:

You want to run a db.adminCommand() in NodeJS using the node-mongodb-native client, e.g. you want to run the NodeJS equivalent of

db.adminCommand({setParameter: 1, internalQueryExecMaxBlockingSortBytes: 100151432});

Solution:

Use conn.executeDbAdminCommand() where db is a MongoDB database object.

db.executeDbAdminCommand({setParameter: 1, internalQueryExecMaxBlockingSortBytes: 100151432});

Full example:

// To install, use npm i --save mongodb
const MongoClient = require('mongodb').MongoClient;

async function configureMongoDB() {
    // Connect to MongoDB
    const conn = await MongoClient.connect('mongodb://localhost:27017/', { useNewUrlParser: true });
    const db = await conn.db('mydb');
    // Configure MongoDB settings
    await db.executeDbAdminCommand({
        setParameter: 1,
        internalQueryExecMaxBlockingSortBytes: 100151432
    });
    // Cleanup
    return conn.close();
}

// Run configureMongoDB()
configureMongoDB().then(() => {}).catch(console.error)

 

Posted by Uli Köhler in Databases, NodeJS

How to read NodeJS child-process.exec stdout/stderr using async/await Promises

You want to run a command like file my.pdf using NodeJS child-process.exec and get its stdout after it’s finished.

Solution:

TL;DR: (await exec('file my.pdf')).stdout

We’re using child-process-promise here in order to simplify our implementation. Install it using npm i --save child-process-promise !

const { exec } = require('child-process-promise');

async function run () {
    const ret = await exec(`file my.pdf`);
    return ret.stdout;
}

run().then(console.log).catch(console.error);

You can also use .stderr instead of .stdout to get the stderr output as a string

Posted by Uli Köhler in Javascript, NodeJS

How to install NodeJS 10.x on Ubuntu in 1 minute

Note: Also see How to install NodeJS 12.x on Ubuntu in 1 minute

Run these shell commands on your Ubuntu computer to install NodeJS 10.x:

curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs

Instead of setup_10.x you can also choose other versions like setup_8.x. However, using this method, you can’t install multiple versions of NodeJS in parallel.

Source: Official nodesource documentation

Posted by Uli Köhler in Linux, NodeJS

How to fix WebPack error describe: optionsSchema.definitions.output.properties.path.description

Problem:

You are trying to build your webpack project, but you see an error message like this:

/home/uli/project/node_modules/webpack-cli/bin/config-yargs.js:89
                                describe: optionsSchema.definitions.output.properties.path.description,
                                                                           ^

TypeError: Cannot read property 'properties' of undefined
    at module.exports (/home/uli/project/node_modules/webpack-cli/bin/config-yargs.js:89:48)
    at /home/uli/project/node_modules/webpack-cli/bin/webpack.js:60:27
    at Object.<anonymous> (/home/uli/project/node_modules/webpack-cli/bin/webpack.js:515:3)
    at Module._compile (internal/modules/cjs/loader.js:723:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:734:10)
    at Module.load (internal/modules/cjs/loader.js:620:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:560:12)
    at Function.Module._load (internal/modules/cjs/loader.js:552:3)
    at Module.require (internal/modules/cjs/loader.js:659:17)
    at require (internal/modules/cjs/helpers.js:22:18)

Solution:

This is a known bug in webpack 4.20.0 – you can circumvent this issue by using webpack 4.19.0.

Look for a line like

"webpack": "^4.7.0",

in your package.json. The caret (^) allows npm to use any 4.x.x version – including the broken 4.20.0.

Replace the aforementioned line by

"webpack": "4.19.0",

to use only webpack 4.19.0.

After that, run npm install and retry building your application.

Posted by Uli Köhler in Javascript, NodeJS

Fixing NodeJS Intl.DateTimeFormat not formatting properly for locales

Symptom:

NodeJS starting from version v9.x supports the ES6 Intl.DateTimeFormat

When you use it with the ‘en-US’ locale, it works properly and prints "August 13, 2018":

const df = new Intl.DateTimeFormat('en-US', {day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC'});
console.log(df.format(new Date("2018-08-13T04:00:00.000Z")));

However, using it with a different locale fails:

const df = new Intl.DateTimeFormat('de', {day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC'});
console.log(df.format(new Date("2018-08-13T04:00:00.000Z")));

While you would expect this to print "13. August 2018" , it will print "2018 M08 13"

Reason:

By default, NodeJS is only built with small-icu support, thereby only installing the en-US locale in order to reduce the installation filesize.

Solution 1 (preferred):

You can use the intl polyfill module to completely replace NodeJS’s implementation of intl:

Installation:
npm i --save intl
Usage:
// Replace Intl by polyfill
Intl = require("intl")

const df = new Intl.DateTimeFormat('de', {day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC'});
console.log(df.format(new Date("2018-08-13T04:00:00.000Z")));

This will print 13. August 2018 as expected.

Solution 2 (alternate):

You can use the full-icu package to continue using the NodeJS ICU implementation (i.e. no polyfill), but just install the ICU data.

While this reduces the total installation filesize, installation is slow and the exact method depends on the NodeJS version and requires more work than just using the intl polyfill.

In order to install, use

npm i --save full-icu

This will take some time to compile the data and then will print instructions like this:

 √ icudt62l.dat (link)
Node will use this ICU datafile if the environment variable NODE_ICU_DATA is set to “node_modules/full-icu”
or with node --icu-data-dir=node_modules/full-icu YOURAPP.js
 For package.json:
{"scripts":{"start":"node --icu-data-dir=node_modules/full-icu YOURAPP.js"}}

By the way, if you have full data, running this in node:
> new Intl.DateTimeFormat('es',{month:'long'}).format(new Date(9E8));
... will show “enero”. If it shows “January” you don't have full data.
News: Please see https://github.com/icu-project/full-icu-npm/issues/6

In order to actually use full-icu, you need to use the --icu-data-dir=node_modules/full-icu argument every time you run node. In order to run node interactively, use

node --icu-data-dir=node_modules/full-icu

If you use scripts in your application (e.g. the start script, i.e. what gets executed if you run npm start), you need to adjust the configuration in package.json:

Instead of

// [...]
"scripts": {
    "start": "node index.js"
}
// [...]

use

// [...]
"scripts": {
    "start": "node --icu-data-dir=node_modules/full-icu index.js"
}
// [...]

Depending on your application, you might need to use a different script name than index.js – common names include server.js and start.js

Posted by Uli Köhler in Allgemein, Javascript, NodeJS

Fixing npm/node-gyp Error: not found: make on Ubuntu

When you run npm install and it tries to install a native package like bcrypt and you see an error message like this:

gyp ERR! build error 
gyp ERR! stack Error: not found: make
gyp ERR! stack     at getNotFoundError (/usr/lib/node_modules/npm/node_modules/which/which.js:13:12)
gyp ERR! stack     at F (/usr/lib/node_modules/npm/node_modules/which/which.js:68:19)
gyp ERR! stack     at E (/usr/lib/node_modules/npm/node_modules/which/which.js:80:29)
gyp ERR! stack     at /usr/lib/node_modules/npm/node_modules/which/which.js:89:16
gyp ERR! stack     at /usr/lib/node_modules/npm/node_modules/isexe/index.js:42:5
gyp ERR! stack     at /usr/lib/node_modules/npm/node_modules/isexe/mode.js:8:5
gyp ERR! stack     at FSReqWrap.oncomplete (fs.js:182:21)

you simple need to install GNU Make. On Ubuntu, the easiest way of doing this is to run

sudo apt install build-essential

This will not only install make but also related tools like gcc and some standard header files and tools.

Posted by Uli Köhler in Linux, NodeJS

Fixing node/npm ImportError: No module named compiler.ast on Ubuntu 18.04

If you run npm install and encounter this error message:

ImportError: No module named compiler.ast

you need to install the python development files using

sudo apt install python-dev

Note: In my case, using apt install python3-dev did not solve the issue.

Posted by Uli Köhler in Linux, NodeJS

How to set cell value to string using js-xlsx

This snippet reads a XLSX file using js-xlsx, sets the C2 cell to abc123 and writes the result to another file:

const XLSX = require('xlsx');

const table = XLSX.readFile('mytable.xlsx');
// Use first sheet
const sheet = table.Sheets[table.SheetNames[0]];

// Option 1: If you have numeric row and column indexes
sheet[XLSX.utils.encode_cell({r: 1 /* 2 */, c: 2 /* C */})] = {t: 's' /* type: string */, v: 'abc123' /* value */};
// Option 2: If you have a cell coordinate like 'C2' or 'D15'
sheet['C2'] = {t: 's' /* type: string */, v: 'abc123' /* value */};

XLSX.writeFile(table, 'result.xlsx');

 

Posted by Uli Köhler in Javascript, NodeJS

How to iterate over XLSX rows using js-xlsx

This snippet allows you to easily iterate over rows in any XLSX files using the js-xlsx library (in this example we don’t iterate over all columns but rather only get the B column as an example):

const table = XLSX.readFile('mytable.xlsx');
const sheet = table.Sheets[table.SheetNames[0]];

var range = XLSX.utils.decode_range(sheet['!ref']);
for (let rowNum = range.s.r; rowNum <= range.e.r; rowNum++) {
    // Example: Get second cell in each row, i.e. Column "B"
    const secondCell = sheet[XLSX.utils.encode_cell({r: rowNum, c: 1})];
    // NOTE: secondCell is undefined if it does not exist (i.e. if its empty)
    console.log(secondCell); // secondCell.v contains the value, i.e. string or number
}

 

Posted by Uli Köhler in Javascript, NodeJS

How to get first XLSX sheet in js-xlsx

If you want to read an XLSX file and just get the first sheet in that file using js-xlsx, you can use this snippet:

const XLSX = require('xlsx');

const table = XLSX.readFile('mytable.xlsx');
// First sheet is extracted here
const sheet = table.Sheets[table.SheetNames[0]];

 

Posted by Uli Köhler in Javascript, NodeJS

How to fix NodeJS request Error: Argument error, options.body

Problem:

You’re using request to send a POST request with a body that should be JSON-encoded, but you are encountering an error similar to this:

Error: Argument error, options.body.
    at setContentLength (/home/uli/myproj/node_modules/request/request.js:434:28)
    at Request.init (/home/uli/myproj/node_modules/request/request.js:439:5)
    at new Request (/home/uli/myproj/node_modules/request/request.js:128:8)
    at request (/home/uli/myproj/node_modules/request/index.js:53:10)
    at Function.post (/home/uli/myproj/node_modules/request/index.js:61:12)

and, additionally this stacktrace:

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string or Buffer
    at write_ (_http_outgoing.js:647:11)
    at ClientRequest.write (_http_outgoing.js:622:10)
    at Request.write (/home/uli/myproj/node_modules/request/request.js:1501:27)
    at end (/home/uli/myproj/node_modules/request/request.js:546:18)
    at Immediate.<anonymous> (/home/uli/myproj/node_modules/request/request.js:575:7)
    at runCallback (timers.js:763:18)
    at tryOnImmediate (timers.js:734:5)
    at processImmediate (timers.js:716:5)

Solution:

The error basically tells you that request can’t determine the length of the body, as you didn’t tell it how to encode the body and it isn’t a simple string or buffer. The solution is to tell request to use JSON body encoding by adding json: true to the options parameter (first argument to request.post). A valid options parameter looks like this:

const opts = {
    url: 'http://localhost:1234/api/myapi',
    body: {/* your body object */},
    json: true // <-- Add this line
};
Posted by Uli Köhler in Javascript, NodeJS

Solving npm Usage of the –dev option is deprecated. Use –only=dev instead.

Problem:

You want to install development dependencies for a NodeJS package using

npm install --dev

but you get this error message:

npm WARN install Usage of the `--dev` option is deprecated. Use `--only=dev` instead.

Solution:

You can use

npm install # Install normal (not development) dependencies
npm install --only=dev # Install only development dependencies

instead. Note that npm install --only=dev will only install development dependencies, so in most cases you want to run both commands.

Posted by Uli Köhler in NodeJS

Using nodemon without a global installation

Problem:

You want to use nodemon in order to automatically reload your NodeJS server, however you don’t want to require a global installation (npm install -g nodemon) but instead install it locally into the node_modules directory:

Solution:

First, install nodemon as dependency (

npm install --save-dev nodemon

We installed it as development dependency for this example, but it will work just as well if you install it as a normal dependency using --save instead of --save-dev.

After that, add a script entry in package.json:

"scripts": {
  "devserver": "./node_modules/nodemon/bin/nodemon.js index.js"
}, /* rest of package.json */

Replace index.js with the name of the file you want to run using nodemon.

Now you can start the development server using

npm run devserver
Posted by Uli Köhler in NodeJS