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.

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!