webpack: Cannot assign to read only property 'exports' of object '#' (mix require and export)

Do you want to request a feature or report a bug? bug

What is the current behavior? Module with code

// 'a.js'
module.exports = { a: 'b' };

// b.js
const a = require('./a.js').a;

export default {
   aa: a
}

Gives error:

Cannot assign to read only property 'exports' of object '#<Object>'

Appeared after upgrade webpack 2.2.0.rc.6 -> 2.2.0.

If ‘harmony-module’ is ES2015 module, then looks like it’s now impossible to mix require and export default in single module. If it so, that’s okay, but should be mentioned in docs.

Please mention other relevant information such as the browser version, Node.js version, webpack version and Operating System. MacOS 10.12.2 node.js 6.9.2 webpack 2.2

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 121
  • Comments: 113 (21 by maintainers)

Commits related to this issue

Most upvoted comments

The code above is ok. You can mix require and export. You can’t mix import and module.exports.

Now that Babel 7.x is out, I’ll just say that this should essentially be resolved. The only time you should see this, with Babel 7.x, is if you’re doing one of:

  1. You’ve actually using import and module.exports in the same file, which is not allowed by Webpack. You can sidestep this by setting "modules": "commonjs", which will make Babel compile the import to a require. This breaks tree shaking, as mentioned above though, so fixing your code would be a better idea.
  2. You’re using useBultins: 'entry'/'usage, or @babel/plugin-transform-runtime, and you are running Babel on CommonJS files (either your own, or in random node_modules, if you’re trying to compile that). Babel assumes files are ES modules by default, meaning those transforms would insert import statements into your file, triggering case 1. above. You can avoid this issue by setting sourceType: "unambiguous" in your Babel configuration, which will tell it to guess the type, like Webpack does, instead of assuming all files are modules.

@Zdend You could be hitting either or both of these cases, it is hard to say.

This issue was newly affecting me after instructing Babel to not transpile module syntax. As sokra described, it only occurred when trying to use CommonJS style module.exports inside of ES modules. require always works. This can be fixed by simply replacing all module.exports = ... to export default ... where applicable, as this is seemingly equivalent for old Babel-style ES module transpilation. (Note though, that importing this module using require will probably give you an option with a default key rather than the default export itself, so it’s probably best you make the switch across the entire codebase at once.)

transform-runtime adds import to your files…

The way I see it, there are a few good solutions:

  • Make transform-runtime support outputting require statements instead of import statements, as an option. This seems unlikely, because it looks like Babel is already relying on the fact that import is declarative.
  • Use transform-es2015-modules-commonjs. This would replace all import statements with require statements. If you are not using any import/export syntax in your app, this solution is probably ideal, because it incurs no extra cost. However, if you are using ES modules, then this will likely impact Webpack 2’s ability to perform tree shaking. (I’m sure everyone is aware of this.)
  • Use ES modules across the board. I think this is the most correct thing to do. Is there a reason why existing CommonJS exports can’t simply be migrated? require does not have to be globally replaced, only module.exports and exports statements.

Taking a quick glance, this is definitely the culprit:

https://github.com/webpack/webpack/commit/a7a41848c73f14ff294b48285f42063e1f0de4b1

detect harmony modules before parsing
exports is now undefined in ESM
module.exports is now read-only in ESM and returns undefined
define is now undefined in ESM

As it says, module.exports is read-only in ES modules. If you’re getting module.exports as read-only in a CommonJS module, THAT would be a bug, but that file is very much not a CommonJS file if it contains any export/import statements. I can confirm that this was the problem in my case and it was fixed by making my ES modules properly use ES exports. As a note require still works just fine inside of any module.

From my PoV it seems like Webpack’s new behavior ultimately makes a lot more sense than the old behavior. It is fairly inane to allow mixing of import statements and CommonJS module.exports, and probably was never intended to work that way.

I solved the problem by adding “@babel/plugin-transform-modules-commonjs

{
  "presets": ["@babel/preset-env"],
  "plugins": ["@babel/plugin-transform-runtime","@babel/plugin-transform-modules-commonjs"]
}

I actually just got this to work by changing my .babelrc file.

{
  "presets": [
    "es2015",
    "es2016",
    "stage-2"
  ],
  "plugins": [
    "transform-async-to-generator",
    "transform-class-properties",
    "transform-object-rest-spread",
    "transform-export-extensions",
    "transform-runtime",
    "lodash"
  ]
}

The big change that worked for me was removing modules: false from es2015

before: "presets": [ [ "env", { "modules": false, } after: "presets": [ [ "env", { "modules": "commonjs", } it seems to be .babelrc’s modules does’t match commonjs. here is the :https://www.babeljs.cn/docs/babel-preset-env docs image

Using babel 7 with webpack v4 this solved mine:

plugins: [ [‘@babel/plugin-transform-modules-commonjs’, { ‘allowTopLevelThis’: true }] ]

Maybe add this info to migration guide?

was suddenly having this issue on a vue-cli project after trying out the 4.0.alpha.1

adding sourceType: 'unambiguous', to my babel.config.js file made the error go away

Adding sourceType: 'unambiguous', to my babel.config.js fixed things for me.

I wouldn’t normally comment like this, but this issue was one of the first links I opened from google and I missed the solution: https://github.com/webpack/webpack/issues/4039#issuecomment-419284940. (way up over a year ago)

before: "presets": [ [ "env", { "modules": false, } after: "presets": [ [ "env", { "modules": "commonjs", } it seems to be .babelrc’s modules does’t match commonjs. here is the :https://www.babeljs.cn/docs/babel-preset-env docs image

"presets": [
    ["@babel/preset-env", {
      "modules": "commonjs"
    }],
    "react-app"
  ],

evn => @babel/preset-env

I think for me what happened was that i am using @babel/preset-env with the option: useBuiltIns: 'usage' This feature lets babel automatically import polyfills for stuff based on the provided targets when they are used in the file.

// babel.config.js
const presets = [
  [
    "@babel/preset-env",
    {
      useBuiltIns: "usage",
      corejs: "3",
    },
  ],
];

const plugins = [
  "@babel/plugin-proposal-class-properties",
];

module.exports = api => {
  api.cache(true);

  return {
    presets,
    plugins,
  }
};

So preset-env would automatically import polyfills from core js inside my commonJS modules. for example this file:

__webpack_require__.r(__webpack_exports__);
/* WEBPACK VAR INJECTION */(function(module) {/* harmony import */ var core_js_modules_es_object_assign__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es.object.assign */ "./node_modules/core-js/modules/es.object.assign.js");
/* harmony import */ var core_js_modules_es_object_assign__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_object_assign__WEBPACK_IMPORTED_MODULE_0__);


// Some code....

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

// Some code....

module.exports = {
  WEBPACK_HOST_APP_LOCAL: WEBPACK_HOST_APP_LOCAL,
  WEBPACK_HOST_FBPAY_LOCAL: WEBPACK_HOST_FBPAY_LOCAL,
  ENVIRONMENTS: ENVIRONMENTS,
  WEBPACK_PORTS: WEBPACK_PORTS,
  WEBPACK_ESLINT_DEV: WEBPACK_ESLINT_DEV,
  WEBPACK_ESLINT_PROD: WEBPACK_ESLINT_PROD
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/webpack/buildin/harmony-module.js */ "./node_modules/webpack/buildin/harmony-module.js")(module)))

So the import automatically inserted at the top of my file and the module exports didn’t play along well i guess… I hope i’m right 🤷‍♂️

Fixed mine by removing { “modules”: false } from this line in .babelrc: [“env”, { “modules”: false }]

You can’t mix import and module.exports

@sokra how difficult would it be to add a webpack compile error for this case? Seems there’s room for a clearer explanation

I solved the problem by adding modules: "cjs" to preset-env

["@babel/preset-env", {
    "loose": true, 
    "targets": {
        "browsers": [
            "ie >= 9",
            "> 1%",
            "last 2 versions"
        ]
    },
    "useBuiltIns": "usage",
    "modules": "cjs" // the default value is auto
}]

npm install webpack@2.1.0-beta.27 --save can release the pain temporarily.

This is the reason: https://github.com/webpack/webpack/pull/3958 But I like this suggestion: https://github.com/webpack/webpack/issues/3917#issuecomment-272746700

the better error info should be “module.exports is readonly when using ES2015 modules. Do not mix import with module.exports.”

@johnwchadwick I just upgraded one of my project to Webpack 2.2.x and it fixed my issue. Thank you.

Replaced module.exports with export default, and the require(...) with require(...).default

I solved this issue by adding "sourceType": "unambiguous" in my babelrc file

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-typescript",
  ],
  "plugins": [
    "@babel/plugin-transform-runtime",
    "@babel/plugin-syntax-dynamic-import"
  ],
  "comments": false,
  "sourceType": "unambiguous"
}

After adding it, @babel/plugin-transform-runtme will inject require expression instead of import in my commonjs files so they won’t mix XD

Had this issue today, adding this in .babelrc fixed it (https://github.com/webpack/webpack/issues/4039#issuecomment-419284940):

"sourceType": "unambiguous"

Removing "modules: false" in .babelrc might work

I’ve written the rule and it works. I think it’d be best to add to eslint-plugin-import so I’ve added an issue there: https://github.com/benmosher/eslint-plugin-import/issues/760

Unfortunately the exports-loader injects module.exports statements so using it with a legacy codebase and injecting that to a es6 project has become impossible.

image image

I can only speak for myself, but for the situation affecting me the module within which module.exports is read-only does not have any import or export statements. It appears that the heuristic that is identifying the file in question as a ‘harmony’ model is misfiring in some situations.

I was also using useBuiltIns: "usage" and didn’t want to go back to core-js@2 and for some reason sourceType: "unambiguous" only moved the error OP (and I) had to the following error:

TypeError: wrappedWellKnownSymbolModule.f is not a function

occurring in most Webpack loaders during build.

After looking and searching for some time I stumbled over the following posts:

Honestly, I don’t understand what is going on here. But excluding core-js from Babel (as shown in the StackOverflow post) in conjunction with setting sourceType: "unambiguous" solved the issues I had with core-js@3.

I’d love if someone could explain the why and what, I cannot. But hopefully this helps the next guy stumbling over this issue.

I had the following .babelrc

{
    "presets": [
        [
            "es2015",
            {
                "modules": false
            }
        ],
        "stage-0",
        "react"
    ],
    "plugins": [
        "add-module-exports",
        "transform-class-properties",
        "transform-object-rest-spread",
        "transform-object-assign"
    ]
}

And was able to fix this by removing the add-module-exports plugin. TBH, I can’t even recall why I had it in there to begin with… but upon removal it started working as expected. Nobody else has mentioned add-module-exports yet so I thought I’d chime in too to point out that it’s not just transform-runtime causing this.

@sokra here are the relevant config files:

.babelrc:

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      }
    }]
  ],
  "plugins": ["transform-runtime", "syntax-dynamic-import"]
}

webpack.config.js (simplified):

'use strict';

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const Path = require('path');
const Webpack = require('webpack');


const config = {
    cache: true,
    devtool: process.env.NODE_ENV !== 'production' ? 'source-map' : false,
    context: Path.join(__dirname),
    output: {
        path: Path.join(__dirname, 'build'),
        filename: '[name].js',
        chunkFilename: '[name].js',
        publicPath: '/static/',
    },
    recordsPath: Path.join(__dirname, 'recordsCache'),
    module: {
        rules: [{
                test: /\js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'ng-annotate-loader',
                    },
                    {
                        loader: 'babel-loader',
                        options: {
                            cacheDirectory: true,
                        },
                    },
                    {
                        loader: 'string-replace-loader',
                        options: {
                            multiple: [
                                { search: /API_URL/g, replace: process.env['PLUNKER_API_URL'] },
                                { search: /EMBED_URL/g, replace: process.env['PLUNKER_EMBED_URL'] },
                                { search: /RUN_URL/g, replace: process.env['PLUNKER_RUN_URL'] },
                                { search: /SHOT_URL/g, replace: process.env['PLUNKER_SHOT_URL'] },
                                { search: /WWW_URL/g, replace: process.env['PLUNKER_WWW_URL'] },
                            ],
                        },
                    },
                ]
            },
        ],
    },
    plugins: [
        new ExtractTextPlugin({
            filename: '[name].css',
            disable: false,
            allChunks: true,
        }),
    ],
    resolve: {
        modules: [
            Path.join(__dirname, 'node_modules'),
            Path.join(__dirname, 'src'),
        ],
    },
};

module.exports = config;

webpack@3.4.1

This will work:

a.js

module.exports = {};

b.js

import a from './a'
console.log(a)

But this will not work:

a.js

// the occurrence of keyword `typeof` will lead to error:
// `Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'`
var x = typeof 1;
module.exports = {};

b.js

import a from './a'
console.log(a)

This will work:

a.js

// noop

b.js

import a from './a'
console.log('imported a:', a);

output

imported a: object {}

But this will cause a warning:

a.js

var x = typeof 1;

b.js

import a from './a'
console.log('imported a:', a);

output

imported a: undefined
[HMR] bundle has 1 warnings
./b.js
"export 'default' (imported as 'a') was not found in './a'

So

I think the keyword typeof will cause the ES/CommonJS module type distinguishing error.

I find myself also being bitten by this issue since upgrading to 2.2.0. In my case, my hypothesis is a require chain that looks like:

CommonJSModule --requires--> ESModule --requires--> AnotherCommonJSModule

I’m sorry that I can’t provide any more details at this time.

@Zdend Note that you’ll miss out on tree shaking by doing that.

Just bumped into this myself. There should probably be an eslint rule that disallows using import in a file with module.exports. Anyone know whether this exists? If not, I’ll make it 😃

I’m not at all familiar with the internals but a quick, manual binary search suggests the regression is here: https://github.com/webpack/webpack/compare/v2.2.0-rc.4...v2.2.0-rc.5

Attn: @sokra, @TheLarkInn I think this issue should be re-opened.

I fixed this by removing module:false from .babelrc

Before

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

After

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

This worked for me as well, do you understand why though? I am so perplexed

I fixed this by removing module:false from .babelrc

Before

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

After

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

I actually just got this to work by changing my .babelrc file.

{
  "presets": [
    "es2015",
    "es2016",
    "stage-2"
  ],
  "plugins": [
    "transform-async-to-generator",
    "transform-class-properties",
    "transform-object-rest-spread",
    "transform-export-extensions",
    "transform-runtime",
    "lodash"
  ]
}

The big change that worked for me was removing modules: false from es2015

this answer worked for me, thanks a lot

I was also running into this problem when using @babel/preset-env with the useBuiltIns: 'usage' option, switching to core-js version 2 is what fixed it for me.

The common causes of this issue are spelled out in https://github.com/webpack/webpack/issues/4039#issuecomment-419284940

While force-loading @babel/plugin-transform-modules-commonjs technically works, it is not what I would recommend since it essentially disables many of the benefits Webpack can provide with tree shaking.

This configuration works for me.

webpack.config.js
module.exports = {
  //...
  resolve: {
    // configuration options
    symlinks: false,
  }
};

Good afternoon,

I am using Vue 2.5.16 and running into a similar problem. After reading through this thread my issue still perplexes me to why my console is throwing this error Cannot assign to read only property 'exports' of object '#<Object>'

I am bringing in this helper function using const helper = require("../assets/helper/helper.js"); while using;

module.exports=simpleNoise: function(){this.perm = new Uint8Array(512);}

I have a larger function that I wish to export into my template, but I have isolated this line to be causing the error. The instantiation of a new Uint8Array() throw the error.

Any guidance on why this might be would be greatly appreciated.

It’s clear that this issue happens when import and module.exports are mixed. As well it’s clear that the possible solution, such as removing modules:false from the ['es2015', {modules: false}] equasion is not good, as it affects tree shaking.

Have two questions though:

  1. I thought that if I will require my module on the front-end side (explicitly) this would solve the issue. As module.exports module would be required. But I see the same error. Why?
  2. Why I don’t see such error for `React, for example. In my code, as the same file I have:
import React, {Component} from 'react';
import Workflow from 'common/modules/workflow';

whily react leads to: image

And workflow leads to: image

In the console I see the error about the workflow inclusion: image

how the react case is different? 3. What is the expected way to have an isomorphic modules and use it both on nodejs and front-end parts. (I don’t want to transpile my nodejs code)

@sokra

Regards,

你把module.export改为export.default试试

https://babeljs.io/docs/en/options#sourcetype

“script” - Parse the file using the ECMAScript Script grammar. No import/export statements allowed, and files are not in strict mode. “module” - Parse the file using the ECMAScript Module grammar. Files are automatically strict, and import/export statements are allowed. “unambiguous” - Consider the file a “module” if import/export statements are present, or else consider it a “script”.

Given that the default for sourcetype is “module”, I wonder why Babel doesn’t treat the presence of module.exports (and the absence of import/export statements) as a signal that the file should be parsed as “script” instead, to avoid throwing this error.

If you don’t want to use babel, my solution works.

I fixed this by making an entry point file like. node index.js

// index.js
require = require('esm')(module)
module.exports = require('./app.js')

and any file I imported inside app.js and beyond worked with imports/exports

I actually just got this to work by changing my .babelrc file.

{
  "presets": [
    "es2015",
    "es2016",
    "stage-2"
  ],
  "plugins": [
    "transform-async-to-generator",
    "transform-class-properties",
    "transform-object-rest-spread",
    "transform-export-extensions",
    "transform-runtime",
    "lodash"
  ]
}

The big change that worked for me was removing modules: false from es2015

I have been struggling to find solution for the last 4 hours, this worked for me thank you!!!

@pfeiferbit That sounds like it would be a compilation issue unrelated to the issues here. Most likely, if you’re using one of the core-js import-injection plugins, and compiling core-js itself, you’ll be injecting core-js imports into core-js itself, creating dependency cycles and errors like that one. You shouldn’t run Babel on core-js so excluding it is the way to go. See https://github.com/babel/babel/issues/8754

@STRsplit Ok, my bad 😃 Hopefully it will help some others though! Can’t help you with babel.config.js.

Yeah that error means you did

presets: [
  '@babel/preset-env',
  { modules: false }
]

instead of

presets: [
  ['@babel/preset-env', { modules: false } ]
]

Note the extra set of square brackets.

Thanks @loganfsmyth

Short answer。

If you are using babel 7 and bable-trunsform-runtime like me, you can set:

"sourceType": "unambiguous",

@bitcot I guessed, I tried, it worked, no explanation…

Faced this issue when imported .js with ES6 class and module.exports = into my vue-cli project. Webpack config:

{
  test: /\.js$/,
  loader: 'babel-loader',
  include: [
    resolve('src'),
    resolve('test'),
    resolve('node_modules/webpack-dev-server/client'),
    resolve('node_modules/my-class-module'),
  ]
},

babel config:

{
  "presets": [
    ["env", {
      "modules": false,
      "useBuiltIns": true,
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
}

Included class:

class MyClass {}
module.exports = MyClass;

@PavelPolyakov you should post a reproduction of your issue instead of snippets. It will make it a lot easier for others to follow and diagnose the issue.

Then the codes not truly isomorphic, because you’re not using the same output on both platforms. You can’t have it both ways 😃

If so this particular feature looks not very thought through. I think there should be a way for the developer to have an isomorphic modules for nodejs and webpack. This is very natural need.

what is the recommended solution for this issue when a dependent npm module is performing an import when they shouldn’t? Is there any way to have webpack automatically alter the import blah from 'blah' to var blah = require('blah').default ?

@kentcdodds PR not really an option as the module devs are trying to decide on which style to adopt, 1.5 months ago, can’t force someone else to accept a PR to resolve a bundling issue.

@TheLarkInn Definitely something we can explore for 7.0. There are several issues around transform-runtime that have been longstanding that we’re hoping to fix.

@ggoodman are you using babel-runtime?

@sokra probably in real code I had something more, that caused the issue. Now I’ve refactored this part to import export and it works fine.

I’ll try check it in more clear example. For now I believe the issue can be closed.