Sending command line arguments to a package.json script

Do you need to pass command line arguments dynamically to a package.json script? This is how to do it.

Static command line arguments can be set with the scripts.

  ...
  "scripts": {
    "cli": "node ./src/build-helper-cli.js -V"
  }
  ...

The script can be called as follows.

 $ npm run cli

Now lets pass it a command line argument dynamically.

$ npm run cli -- add-eslint-disable --dest build

The trick are the two dashes (–) which tell npm what follows are command line arguments for the script being executed. That is it. Easy right?

Advertisements

Local npm module (link) causes “Cannot assign to read only property ‘exports’ of object”

I’m creating a Vue plugin using Vue CLI 3. I was using the vue-cli-service build with target type lib to build the reusable plugin that could be imported into another project. While developing the plugin I was testing it as a local npm module (link) and I ran into this error “Cannot assign to read only property ‘exports’ of object”. I was using npm link to create a local module.

It took a lot of googling around to find the solution. It turns out it has to do with Babel and webpack. As always once you know the solution it is simple. I had to add sourceType unambiguous to my babel.config.js file. This is my babel.config.js file:

module.exports = {
  presets: [
    '@vue/app'
  ],
  sourceType: 'unambiguous'
}

This comment of a webpack issue contains the explanation. Please check it out to understand why this works.

I hope this helps someone and saves them (lots of) time.

Find the location of NPM global packages on Windows

Today I wanted to add a file from a package that I had installed globally with NPM to Webstorm. Where does NPM install the global packages on Windows? How to find the location?

As always, once you know how it is simple. Just execute the following command from cmd.

npm config ls -l | grep prefix

That will show you the location. Which is probably something like C:\Users\\AppData\Roaming\npm.

Installing a module from a git repo with npm

I recently needed to add internationalization (i18n) functionality to an existing GitHub repository. I made the changes and put in a pull request. That pull request has not been processed yet. In the mean time I wanted to use my updated version. After some googling around I found out it is possible to manage modules directly from GitHub (git) with npm.

Such a cool feature. It means I can use my updated version until the main repository is updated. It is really easy, just change the line in the package.json file if you already have the module imported.

  ...
  "dependencies": {
    "@babel/polyfill": "^7.0.0-rc.1",
    "@feathersjs/feathers": "^3.2.3",
    "@feathersjs/rest-client": "^1.4.5",
    "axios": "^0.18.0",
    "vue": "^2.5.17",
    "vue-analytics": "^5.16.0",
    "vue-i18n": "^8.2.1",
    "vue-meta": "^1.5.5",
    "vue-recaptcha": "^1.1.1",
    "vue-router": "^3.0.1",
    "vue2-flip-countdown": "https://github.com/nidkil/vue2-flip-countdown",
    "vuelidate": "^0.7.4",
    "vuetify": "^1.3.1"
  },
  ...

And then run npm install command. The module will be replaced with the git version.

If you have not installed the module yet just execute the following command.

npm install --save <module name> <git repo> 

Example:

npm install --save vue2-flip-countdown https://github.com/nidkil/vue2-flip-countdown

Is this cool or what?

Override module loaded by require

I was using the module redact-secrets with Winston logger. This module makes sensitive data like passwords unreadable in logfiles. Very cool and handy module. It makes use of another module is-secret that contains a collection of patterns to determine what sensitive data is. One piece of sentive data was missing from is-secret: pass. I could fix it on my side, but I prefer the original GitHub project to be updated so others can also profit from it. So I submitted an issue on GitHub. While waiting for the fix I needed to continue with my development work. So I used another handy module override-require. This module overrides the resolution logic of require. So you can use it to override a dependency of a module. I used it in the following to overrule is-secret used by redact-secrets.

const overrideRequire = require('override-require');

// Check if a request needs to be overridden
const isOverride = (request) => {
return request === 'is-secret';
};

// If isOverride is true, load the module with the overridden module
const resolveRequest = (request) => {
return require('./overrule/is-secret');
};

// Initialize overide-require
const restoreOriginalModuleLoader = overrideRequire(isOverride, resolveRequest);

const { createLogger, format, transports } = require('winston');
// When redacts-secrets is loaded override-require will kickin and load our own module
const redact = require('redact-secrets')('******');
const fs = require('fs');
const path = require('path');

// Disable override require
restoreOriginalModuleLoader();

 

That’s it. Pretty cool isn’t it?

Webstorm not recognising Vuetify component html tags

Recently I moved from VS Code to Webstorm. What a brilliant IDE. It really improved my development flow. One thing that has been irritating me is that the Vuetify component html tags are not recognised, which results in a s**t load of warning messages of unrecognised html tags.

After Googling around I found an issue on the Vuetify GitHub Issues that provides a simple workaround. All you need to do is create a file somewhere in the project that describes the Vuetify components. You don’t need to import the file. It just has to be accessible to Webstorm so that it can be analysed.

There is a fix in the works for the Vuetify api-generator that provides this file out of the box. Just checkout the Javascript and Typescript files.

I placed the Javascript file in my plugin directory naming it vuetify-fake-components-for-webstorm.js. After that all the warnings of unrecognised html tags disappeared like magic. Wooho!

Testing Mongoose plugin with Jest and shared data

Grrrr just spent the better half of 2 hours trying to figure out why my Jest tests were not working for a Mongoose plugin I’m developing. It turns out that the schema configuration I was sharing between the tests was the culprit.

let Mongoose = require('mongoose');
let Schema = Mongoose.Schema;
const _ = require('lodash');
var mongooseI18n = require('../src/mongoose/mongoose-i18n-localize');

Mongoose.set('debug', true);

const simpleSchemaConfig = {
  firstName: { type: String, i18n: true, required: true },
  lastName: { type: String, i18n: false, required: true }
};

describe('Mongoose i18n localize', () => {
  beforeEach(() => {
    // Clear compiled models to avoid OverwriteModelError
    Mongoose.models = {};
  });
  
  it('should throw an exception if the locales array is not defined', () => {
    const SimpleTestSchema = new Schema(simpleSchemaConfig);
    expect(() => {
      SimpleTestSchema.plugin(mongooseI18n, {
        defaultLocale: 'nl',
        allLocalesRequired: false
      });
    }).toThrow('The required option locales array not provided or empty');
  });

  it('should throw an exception if the locales array is an empty array', () => {
    const SimpleTestSchema = new Schema(simpleSchemaConfig);
    expect(() => {
      SimpleTestSchema.plugin(mongooseI18n, {
        locales: [],
        defaultLocale: 'nl',
        allLocalesRequired: false
      });
    }).toThrow('The required option locales array not provided or empty');
  });

  ...

});

The plugin makes changes to the schema configuration and these changes were made to the shared config. The fix I used was to make a copy of the shared config with lodash’s cloneDeep function. The only change I needed to make was the schema initialization.

const SimpleTestSchema = new Schema(_.cloneDeep(simpleSchemaConfig));

Now my tests run like a charm 🙂

Bonus

When I initially ran into the above problem, I was also initializing the schema globally. I thought that was the problem, so I moved the following statement to each test case.

const SimpleTestSchema = new Schema(simpleSchemaConfig);

That caused the exception OverwriteModelError, because I was trying to initialize a compiled model. The fix for this problem was to remove all models between test cases using beforeEach. The following code does the trick.

beforeEach(() => {
  // Clear compiled models to avoid OverwriteModelError
  Mongoose.models = {};
});

Hope this helps someone.