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

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.

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');

 

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
}

 

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
};

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.

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

How to fix npm “Cannot find module ‘graceful-fs'” error

Problem:

When running any npm command, you get a stacktrace similar to the following:

Error: Cannot find module 'graceful-fs'
at Function.Module._resolveFilename (module.js:338:15)
at Function.Module._load (module.js:280:25)
at Module.require (module.js:362:17)
at require (module.js:378:17)
at Object.<anonymous> (/usr/share/npm/lib/utils/ini.js:32:10)
   at Module._compile (module.js:449:26)
at Object.Module._extensions..js (module.js:467:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:362:17)

Read more