Javascript

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 ‘ng upgrade’ error ‘The specified command (“upgrade”) is invalid’

Problem:

You want to run ng upgrade to update your angular project libraries, but you see this error message:

The specified command ("upgrade") is invalid. For a list of available options,
run "ng help".

Did you mean "update"?

Solution:

You need to run

ng update

instead of ng upgrade (which is not a valid command).

This is what Did you mean "update"? in the error message is intended to point you towards.

Posted by Uli Köhler in Angular, Javascript

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

Koa minimal example

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
  ctx.body = {status: 'success'};
});

app.listen(8080);

To install (see this previous post for a guide on installing NodeJS & NPM):

npm install --save koa
node index.js

Then go to http://localhost:8080/ to see the test page.

Posted by Uli Köhler in Javascript

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 fix NodeJS MongoDB ‘Cannot read property ‘high_’ of null’

When encountering an error message like

TypeError: Cannot read property 'high_' of null
    at Long.equals (/home/uli/dev/NMUN/node_modules/bson/lib/bson/long.js:236:31)
    at nextFunction (/home/uli/dev/NMUN/node_modules/mongodb-core/lib/cursor.js:473:16)
    at Cursor.next (/home/uli/dev/NMUN/node_modules/mongodb-core/lib/cursor.js:763:3)
    at Cursor._next (/home/uli/dev/NMUN/node_modules/mongodb/lib/cursor.js:211:36)
    at nextObject (/home/uli/dev/NMUN/node_modules/mongodb/lib/operations/cursor_ops.js:192:10)
    at hasNext (/home/uli/dev/NMUN/node_modules/mongodb/lib/operations/cursor_ops.js:135:3)
    (...)

you likely have code like this:

const cursor = db.getCollection('mycollection').find({})
while (cursor.hasNext()) {
    const doc = cursor.next();
    // ... handle doc ...
}

The solution is quite simple: Since find(), cursor.hasNext() and cursor.next() all return Promises, you can’t use their results directly.

This example shows you how to do it properly using async/await:

const cursor = await db.getCollection('mycollection').find({})
while (await cursor.hasNext()) {
    const doc = await cursor.next();
    // ... handle doc ...
}

In order to do this remember that the function containing this code will need to be an async function. See the Mozilla documentation or google for Javascript async tutorial in order to learn about the details!

Posted by Uli Köhler in Databases, Javascript

Fixing Promise ‘TypeError: myfunc(…).then(…).reject is not a function’

When encountering this error message in Javascript related to Promises:

TypeError: myfunc(...).then(...).reject is not a function

The solution is quite simple: When you want to catch the error from the promise, you need to use catch instead of reject like this:

myfunc(...).then(...).catch(...)

 

Posted by Uli Köhler in Javascript

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

Puppeteer: Get text content / inner HTML of an element

Problem:

You want to use puppeteer to automate testing a webpage. You need to get either the text or the inner HTML of some element, e.g. of

<div id="mydiv">
</div>

on the page.

Solution:

# Get inner text
const innerText = await page.evaluate(() => document.querySelector('#mydiv').innerText);

# Get inner HTML
const innerHTML = await page.evaluate(() => document.querySelector('#mydiv').innerHTML);

Note that .innerText includes the text of sub-elements. You can use the complete DOM API inside page.evaluate(...). You can use any CSS selector as an argument for document.querySelector(...).

Posted by Uli Köhler in Javascript

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 MomentJS interpreting dates as local time

Problem:

You live in a non-UTC timezone. Dates parsed using MomentJS like

moment("2017-01-01")

are interpreted as local time as opposed to UTC (as would be appropiate based on the Z in the ISO8601 timestamp) and therefore trying to format them yields an offset timestamp:

moment("2017-01-01").toDate().toISOString()
// "2016-12-31T23:00:00.000Z" <-- Offset by 1 hour (MEZ - UTC)

This causes you trouble as often the original date is not preserved: In the example above, the correct date would be 2017-01-01 but it is 2016-12-31 instead.

Solution 1: Force moment to parse the date as UTC

moment.utc("2017-01-01").toDate().toISOString()
// "2017-01-01T00:00:00.000Z" <-- Correct

Solution 2: Manually subtract the timezone offset

let m = moment("2017-01-01");
// Subtract the difference between timezone and UTC
m = m.subtract(m.toDate().getTimezoneOffset(), 'minutes');
// m.toDate().toISOString() === "2017-01-01T01:00:00.000Z" - CORRECT

Use this if Solution 1 does not work or you can’t modify the parsing code: This solution works even if the date has already been parsed.

DO NOT use

new Date().getTimezoneOffset() // NEVER USE THIS! MIGHT USE THE WRONG OFFSET!

because it uses the timezone offset of the current date instead of the date parsed by MomentJS. Obviously this is wrong in countries with summertime, since dates may have different offsets depending on the date. Moreover, in rare cases the client’s computer timezone may have changed due to travel etc in the meantime and might therefore might not represent the correct offset to use.

Posted by Uli Köhler in Javascript

Fixing Angular Error: Unexpected value ‘undefined’ declared by the module

Symptom:

For a module of yours, you get an error message like this on load:

Error: Unexpected value 'undefined' declared by the module 'MyModule'
    at syntaxError (compiler.js:1021)
    at compiler.js:10623
    at Array.forEach (<anonymous>)
    at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getNgModuleMetadata (compiler.js:10621)
    at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._loadModules (compiler.js:23876)
    at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._compileModuleAndComponents (compiler.js:23857)
    at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler.compileModuleAsync (compiler.js:23817)
    at CompilerImpl.push../node_modules/@angular/platform-browser-dynamic/fesm5/platform-browser-dynamic.js.CompilerImpl.compileModuleAsync (platform-browser-dynamic.js:143)
    at core.js:4999
[...]

Solution:

First, try to restart ng serve. In some cases this will outright fix the issue.

The error message is caused by some element of your @NgModule declarations: [ /* ... */ ] to be undefined (… declared by the module in the error message should be taken literally, it’s in the declarations!).

For example, if you have a @NgModule declaration like this:

@NgModule({
  imports: [
    RecallCommonModule,
    CommonModule,
    MyRoutingModule
  ],
  declarations: [,
    MyDetailComponent,
    MySearchComponent
  ],
  providers: []
})

The issue is in the stray comma in the declarations line: When compiled, it results in [undefined, MyDetailComponent, MySearchComponent]. Removing the stray comma fixes the issue.

Another possible cause of this error is if one of the values in declarations is undefined.

For example if you have an import statement like

import { MyComponent } from './my.component';

in your module file, and, for some reason, MyComponent is undefined, this will cause the same error message to appear.

In order to track down which of your components in the declarations array, you could temporarily add a statement like this before your @NgModule :

if(MyComponent === undefined) {
    console.log("MyComponent is undefined!");
}

In case you don’t have any of the issues above, follow the standard procedure:
Comment out every element in your declarations array and see which causes the error message to disappear.

Posted by Uli Köhler in Angular, Javascript

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

How to fix Angular6 “_getAriaLabel is not a function” with production build

Problem:

You are building an Angular6 application and in development mode everything works fine. However, if you build in production mode:

ng build --prod --aot

you see an error like this in the client:

main.4d1baabffbba5677af03.js:1 ERROR TypeError: i.ɵnov(...)._getAriaLabel is not a function
    at Object.updateRenderer (main.4d1baabffbba5677af03.js:1)
    at Object.updateRenderer (main.4d1baabffbba5677af03.js:1)
[...]

Solution:

The issue appears to be caused by incorrectly updated NodeJS modules. You can fix it by simply deleting your node_modules folder:

rm -rf node_modules

Furthermore, it’s recommended to update @angular/cli as the bug does not seem to be present in newer versions of @angular/cli:

sudo npm i -g @angular/cli

Source & discussion on GitHub

Posted by Uli Köhler in Angular, Javascript

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