Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot assign to read only property 'exports' of object '#<Object>' (mix require and export) #4039

Closed
DimitryDushkin opened this issue Jan 19, 2017 · 113 comments
Labels

Comments

@DimitryDushkin
Copy link

@DimitryDushkin DimitryDushkin commented Jan 19, 2017

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

@DimitryDushkin DimitryDushkin changed the title Cannot assign to read only property 'exports' of object '#<Object>' Cannot assign to read only property 'exports' of object '#<Object>' (mix require and export) Jan 19, 2017
@sokra
Copy link
Member

@sokra sokra commented Jan 19, 2017

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

Loading

@DimitryDushkin
Copy link
Author

@DimitryDushkin DimitryDushkin commented Jan 19, 2017

@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.

Loading

@jchv
Copy link

@jchv jchv commented Jan 19, 2017

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.)

Loading

@ggoodman
Copy link

@ggoodman ggoodman commented Jan 19, 2017

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.

Loading

@ggoodman
Copy link

@ggoodman ggoodman commented Jan 20, 2017

I'm not at all familiar with the internals but a quick, manual binary search suggests the regression is here: v2.2.0-rc.4...v2.2.0-rc.5

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

Loading

@jchv
Copy link

@jchv jchv commented Jan 20, 2017

Taking a quick glance, this is definitely the culprit:

a7a4184

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.

Loading

@DimitryDushkin
Copy link
Author

@DimitryDushkin DimitryDushkin commented Jan 20, 2017

Maybe add this info to migration guide?

Loading

@ggoodman
Copy link

@ggoodman ggoodman commented Jan 20, 2017

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.

Loading

@sokra
Copy link
Member

@sokra sokra commented Jan 20, 2017

@ggoodman are you using babel-runtime?

Loading

@ggoodman
Copy link

@ggoodman ggoodman commented Jan 20, 2017

@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;

Loading

@joshlasdin
Copy link

@joshlasdin joshlasdin commented Jan 20, 2017

Chiming in as I'm getting this error in the browser as well, but neither the requiring module, nor the required module have any import or export statements in them. I am using transform-runtime if that's relevant.

Loading

@sokra
Copy link
Member

@sokra sokra commented Jan 20, 2017

transform-runtime adds import to your files...

Loading

@ggoodman
Copy link

@ggoodman ggoodman commented Jan 20, 2017

@sokra I was not aware that this plugin would add import to my files.

If removing that plugin is a solution, what alternative can we consider so that we don't have babel-runtime stuff like _classCallCheck included for every source file using ES6 features like classes?

Loading

@jchv
Copy link

@jchv jchv commented Jan 21, 2017

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.

Loading

@blckt
Copy link

@blckt blckt commented Jan 23, 2017

How i can reproduce module.exports behavior?
I had entry point with name LoginPage, i need to build library with the same name, after that i init it on the page using new LoginPage();
Now i should use new LoginPage.LoginPage() or new LoginPage.default();
How i can resolve this issue?

Loading

@sokra
Copy link
Member

@sokra sokra commented Jan 26, 2017

adding import (by transform-runtime) also means that the file is switched into strict mode, which could break you code. In my opinion transform-runtime should be changed.

Loading

@rozzzly
Copy link

@rozzzly rozzzly commented Jan 29, 2017

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.

Loading

@wangdahoo
Copy link

@wangdahoo wangdahoo commented Feb 4, 2017

M

Loading

@albanx
Copy link

@albanx albanx commented Jun 26, 2020

I have a mix of imports and module.export in same files, in almost 60 files, and I have got no issue so far.

Loading

@chaim0m
Copy link

@chaim0m chaim0m commented Jul 16, 2020

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

Loading

@AlexanderKidd
Copy link

@AlexanderKidd AlexanderKidd commented Jul 17, 2020

I am not mixing import with modules.export, I use the following with require and it works on dev server:

const React = require('react')
const charCodeOffset = 97

module.exports = {
  ...
}

I used a regular create-react-app, and added some barebones webpack/babel which I'm not 100% sure are even being used but I can run npm run build.

module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

babel.rc:

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

babel.config.json:

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

  const presets = ["@babel/preset-env", "@babel/preset-react"];
  const plugins = [];

  return {
    sourceType: 'unambiguous',
    presets,
    plugins
  };
}

package.json:

{
  "name": "example",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "animejs": "^3.2.0",
    "bootstrap": "^4.5.0",
    "express": "^4.17.1",
    "prop-types": "^15.7.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-draggable": "^4.4.3",
    "react-resize-aware": "2.7.0",
    "react-router": "^5.2.0",
    "react-router-dom": "^5.2.0",
    "react-scripts": "3.4.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@babel/core": "^7.10.5"
  },
  "babel": {
    "presets": [
      "@babel/preset-env",
      "@babel/preset-react"
    ],
    "plugins": []
  }
}

Any ideas why that error would still show up in production? Both on AWS Amplify server and local production run.

Loading

@AlexanderKidd
Copy link

@AlexanderKidd AlexanderKidd commented Jul 19, 2020

For now I changed the module.exports file to export default and it worked. I have a lot of mixed syntax between ES6 and CommonJS probably and I will have to look into properly setting up Babel and Webpack...

Loading

@flyskywhy
Copy link

@flyskywhy flyskywhy commented Sep 3, 2020

In my react-native-web case, just use an additional webpack rule, then the TypeError: Cannot assign to read only property 'exports' of object is fixed. Maybe you can ref to it.

npm install --save-dev react-app-rewired

Create a config-overrides.js in your project root

// used by react-app-rewired

const webpack = require('webpack');
const path = require('path');

module.exports = {
  webpack: function (config, env) {
    config.module.rules[1].use[0].options.baseConfig.extends = [
      path.resolve('.eslintrc.js'),
    ];

    // To let alias like 'react-native/Libraries/Components/StaticRenderer'
    // take effect, must set it before alias 'react-native'
    delete config.resolve.alias['react-native'];
    config.resolve.alias['react-native/Libraries/Components/StaticRenderer'] =
      'react-native-web/dist/vendor/react-native/StaticRenderer';
    config.resolve.alias['react-native'] = path.resolve(
      'web/aliases/react-native',
    );

    // Let's force our code to bundle using the same bundler react native does.
    config.plugins.push(
      new webpack.DefinePlugin({
        __DEV__: env === 'development',
      }),
    );

    // Need this rule to prevent `Attempted import error: 'SOME' is not exported from` when `react-app-rewired build`
    // Need this rule to prevent `TypeError: Cannot assign to read only property 'exports' of object` when `react-app-rewired start`
    config.module.rules.push({
      test: /\.(js|tsx?)$/,
      // You can exclude the exclude property if you don't want to keep adding individual node_modules
      // just keep an eye on how it effects your build times, for this example it's negligible
      // exclude: /node_modules[/\\](?!@react-navigation|react-native-gesture-handler|react-native-screens)/,
      use: {
        loader: 'babel-loader',
      },
    });

    return config;
  },
  paths: function (paths, env) {
    paths.appIndexJs = path.resolve('index.web.js');
    paths.appSrc = path.resolve('.');
    paths.moduleFileExtensions.push('ios.js');
    return paths;
  },
};

Also create a web/aliases/react-native/index.js

// ref to https://levelup.gitconnected.com/react-native-typescript-and-react-native-web-an-arduous-but-rewarding-journey-8f46090ca56b

import {Text as RNText, Image as RNImage} from 'react-native-web';
// Let's export everything from react-native-web
export * from 'react-native-web';

// And let's stub out everything that's missing!
export const ViewPropTypes = {
  style: () => {},
};
RNText.propTypes = {
  style: () => {},
};
RNImage.propTypes = {
  style: () => {},
  source: () => {},
};

export const Text = RNText;
export const Image = RNImage;
// export const ToolbarAndroid = {};
export const requireNativeComponent = () => {};

Now you can just run react-app-rewired start instead of react-scripts start

Loading

@nabilfreeman
Copy link

@nabilfreeman nabilfreeman commented Oct 7, 2020

@flyskywhy thanks a lot, this saved me a day of work!!!! Sending karma your way 👏

Loading

alwaysblank added a commit to 11in/elfin that referenced this issue Oct 25, 2020
Had a weird issue w/ imports or something when setting this up, that was
solved with this: webpack/webpack#4039 (comment)

Looks like it had to do w/ Babel transpiling (or not) modules in
node_modules.
@lcjnil
Copy link

@lcjnil lcjnil commented Oct 29, 2020

Just a remind, see #4039 (comment) to fix this problem

Loading

kidd added a commit to kidd/oftheday that referenced this issue Nov 15, 2020
Use a helper module with the links.  The culprit was that `require`
doesn't mix with `module.exports`.

You can mix require and export. You can't mix import and
module.exports. webpack/webpack#4039 . Still
have to see why index.js works fine though.
stefanmaric added a commit to stefanmaric/stream-tag that referenced this issue Dec 18, 2020
Hardcoded module.exports breaks webpack when importing the ES module build.

I've removed the add-module-exports plugin since it works only when the default export is the only export, which isn't the case for `stream-tag`.

Instead, I'm patching the commonjs build after the fact.

Related: webpack/webpack#4039
stefanmaric added a commit to stefanmaric/stream-tag that referenced this issue Dec 18, 2020
…e build.

I've removed the `add-module-exports` plugin since it works only when the default export is the only export, which isn't the case for `stream-tag`.

Instead, I'm patching the commonjs build after the fact.

Related: webpack/webpack#4039
stefanmaric added a commit to stefanmaric/stream-tag that referenced this issue Dec 18, 2020
Hardcoded `module.exports` breaks webpack when importing the ES module build.

I've removed the `add-module-exports` plugin since it works only when the default export is the only export, which isn't the case for `stream-tag`.

Instead, I'm patching the commonjs build after the fact.

Related: webpack/webpack#4039
@aeaton-overleaf
Copy link

@aeaton-overleaf aeaton-overleaf commented Jan 18, 2021

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.

Loading

@yyccQQu
Copy link

@yyccQQu yyccQQu commented Feb 22, 2021

just like this

const Datastore = require("nedb");

function DB(database) {
	let options = {
		filename: database,
		autoload: true,
	};
	this.db = new Datastore(options);
}

DB.prototype.limit = function(offset, limit) {
	this.offset = offset || 0;
	this.limit = limit || 15;
	return this;
};

DB.prototype.sort = function(orderby) {
	this.orderby = orderby;
	return this;
};
/**
 * query: Object类型  查询条件 支持使用比较运算符($lt, $lte, $gt, $gte, $in, $nin, $ne), 逻辑运算符
 * offset: 偏移量 忽略多少条  用于分页
 * limit: 返回条数  用于分页
 * 返回: docs 数组  返回查询到的数据
 * * */
DB.prototype.find = function(query, select) {
	return new Promise((resolve, reject) => {
		let stmt = this.db.find(query || {});
		if (this.orderby !== undefined) {
			stmt.sort(this.orderby);
		}
		if (this.offset !== undefined) {
			stmt.skip(this.offset).limit(this.limit);
		}
		if (select != undefined) {
			stmt.projection(select || {});
		}
		stmt.exec((err, docs) => {
			if (err) {
				return reject(err);
			}
			resolve(docs);
		});
	});
};
/**
 * query: object  查询条件
 * 查找一条
 * 返回: 查到数据
 * * */
DB.prototype.findOne = function(query, select) {
	return new Promise((resolve, reject) => {
		let stmt = this.db.findOne(query || {});
		if (this.sort !== undefined) {
			stmt.sort(this.sort);
		}
		if (select != undefined) {
			stmt.projection(select || {});
		}
		stmt.exec((err, doc) => {
			if (err) {
				return reject(err);
			}
			resolve(doc);
		});
	});
};

/**
 * 插入数据
 * value: 插入的数据
 * 使用array,实现批量插入。一旦其中一个操作失败,所有改变将会回滚。
 * * */
DB.prototype.insert = function(values) {
	return new Promise((resolve, reject) => {
		this.db.insert(values, (err, newDoc) => {
			if (err) {
				return reject(err);
			}
			resolve(newDoc);
		});
	});
};

/**
 * 更新数据
 * query: object  查询的数据
 * values: 更新的数据
 * options : object  muti(默认false),是否允许修改多条文档;upsert(默认为false)
 * * */
DB.prototype.update = function(query, values, options) {
	return new Promise((resolve, reject) => {
		this.db.update(
			query || {},
			values || {},
			options || {},
			(err, numAffected) => {
				if (err) {
					return reject(err);
				}
				resolve(numAffected);
			}
		);
	});
};

/**
 * 根据options配置删除所有query匹配到的文档集。
 * query: 与find和findOne中query参数的用法一致
 * options: 只有一个可用。muti(默认false),允许删除多个文档
 * * */
DB.prototype.remove = function(query, options) {
	return new Promise((resolve, reject) => {
		this.db.remove(query || {}, options || {}, (err, numAffected) => {
			if (err) {
				return reject(err);
			}
			resolve(numAffected);
		});
	});
};

// module.exports = (database) => {
// 	return new DB(database);
// };
const nedb = (database='testdb') => {
	return new DB(database);
};
export default nedb
const db = require("./nedb.js").default("testdb");

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet