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.

Advertisements

Using webpack-chain to overrule key name (app) used for default webpack entry by vue-cli 3

Sometimes you have an irritating problem that you have to solve even though it doesn’t really add value. Yesterday I had one of these problems. I wanted to overrule the key name (app) used for the default webpack entry by vue-cli 3 using webpack-chain. This key determines the name of the file that is generated by the build command. It seemed like such a simple change, which it is once you understand how, but it took me sometime to figure out.

I’m writing a Vue plugin and wanted the build version to be called ‘plugin’ instead of ‘app’. I thought it would be a quick change. Took me a little more time than expected, because of the way webpack-chain works and me not reading the documentation properly.

Anyway here are the steps if you want to do the same.

1. Rename the ‘src’ directory to ‘plugin’ (optional, but I like it)
2. Add the `vue.config.js` file and add the following code
module.exports = {
   chainWebpack: config => {
     // Remove the old 'app' entry
     config
       .entryPoints
       .delete('app')
     // Add the new 'plugin' entry
     config
       .entry('plugin')
       .add('./plugin/index.js')
     config
       .resolve
       .alias
       .set('@', path.resolve(__dirname, 'plugin'))
   },
   ...
}

It first removes the old webpack entry (app), adds a new entry (plugin) and finally changes the alias from ‘src’ to ‘plugin’.

3. Update the `moduleNameMapper` in the `jest.config.js`
...
moduleNameMapper: {
'^@/(.*)$': '/plugin/$1'
},
...

That should do the trick. Do you want to test it?

Generate the resolved webpack file using the following command.

$ vue inspect > webpack.config.resolved

The final entry in the resolved webpack file should be the entry point.

{ 
  ...
  entry: {
    plugin: [
      './plugin/index.js'
    ]
  }
}

For convenience I add the following entry to the `scripts` in the `package.json` file.

{ 
  ...
  "scripts": {
    ...
    "inspect": "vue inspect > webpack.config.resolved"
 }
 ...
}

Now you can run npm run inspect to check the resolved webpack configuration file. Handy!

To ensure everything works as expect test and build the project using the following commands.

$ npm run test:unit

and

$ npm run build

In the build directory dist there should be a file js/plugin..js.

Hope this helps someone. Happy coding!

Adding hot reload to featherjs

Feathers is a very cool framework to quickly and easily setup API’s. Version 3 comes with a very cool CLI that generates all the scafolding code for you and makes it extremely easy to add services, hooks, etc. One thing that is missing is hot reloading to simplify the development proces. This is were nodemon comes to the rescue.

nodemon is a tool that helps developing node.js based applications by automatically restarting the node application when file changes in the directory are detected.

Setting nodemon up with Feather and Webpack si a simple two step proces.

  1. Install nodemon
    npm install --save-dev nodemon
  2. Add one line to the scripts section of Webpacks package.json file
    ...
      },
      "scripts": {
        "test": "npm run eslint && npm run mocha",
        "dev": "node ./node_modules/nodemon/bin/nodemon.js src/",
        "eslint": "eslint src/. test/. --config .eslintrc.json",
        "start": "node src/",
        "mocha": "mocha test/ --recursive"
      }
    ...

Now start Feathers using the following command.

npm run dev

That’s it, simple right?

Add Webpack alias using Quasar framework

Trying out Quasar Framework (QF) I ran into a minor challenge setting up the Webpack @ alias like Vue provides out of the box for imports.

import firebaseCfg from '@/firebase/config'

QF uses quasar.conf.js to simplify working with Webpack. So there is no Webpack config file that the alias can be added to directly.

Example of the Webpack resolve alias configuration that Vue provides out of the box.

  ...
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  ...

After some Googeling I found the solution. Add the following to quasar.conf.js file to get the @ alias to work.

At the top of the file.

let path = require('path')

To the build section.

  ...
  build: {
    scopeHoisting: true,
    vueRouterMode: 'history',
    // vueCompiler: true,
    // gzip: true,
    // analyze: true,
    // extractCSS: false,
    extendWebpack (cfg) {
      cfg.module.rules.push({
        enforce: 'pre',
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /(node_modules|quasar)/
      })
      // Create aliases for importing
      cfg.resolve.alias = {
        ...cfg.resolve.alias,
        '@': path.resolve(__dirname, 'src')
      }
    }
  },
  ...

Pretty straight forward once you know how 🙂