webpack: import + module.exports in the same module caused error
Asked Answered
S

4

80

I'm developing a website with webpack. When I have a code like this:

import $ from 'jquery';
function foo() {};
module.exports = foo;

I got the error Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'.

Turns out that changing import $ from 'jquery' to var $ = require('jquery') don't cause any errors.

Why import with module.exports causes this error? Is anything wrong in using require instead?

Steffy answered 24/2, 2017 at 23:30 Comment(2)
@MatthewHerbst changed the main question. – Steffy
JS should've just kept require and nothing else. Now it's a mess with these two different, incompatible ways. Somethings need require, others need import, and your babel settings will make a difference too. – Ab
N
135

You can't mix import and module.exports. In the import world, you need to export things.

// Change this
module.exports = foo;

// To this
export default foo;
Nagey answered 24/2, 2017 at 23:41 Comment(3)
More about this (and how to export more than one function or object): 24ways.org/2014/javascript-modules-the-es6-way – Livraison
Give a try to this veery helpfull link πŸ‘† – Polysemy
This doesn't work for modele.exports = { foo: function () {} } – Iluminadailwain
M
82

This happens if other modules down stream have an unexpected require tree. Babel changes require to import where it isn't supposed to which causes the aforementioned issue @Matthew Herbst. To solve this add "sourceType": "unambiguous" to your babelrc file or babel.config.js so that @babel/plugin-transform-runtime won't do this change of require expression to import in your commonjs files. eg:

module.exports = {
  presets: [
    '@quasar/babel-preset-app'
  ],

  "sourceType": "unambiguous"
}
Mordy answered 23/5, 2019 at 21:54 Comment(2)
Thanks. However it is good solution for modern browsers, it doesn't load on IE11 or below – Leasehold
Wish this was the accepted answer instead of the one that requests removing module.exports, as changing CommonJS modules into ES6 isn't always an option. Thanks for the solution! – Farmhand
L
4

You can use require with export. But not import and module.exports.

Labe answered 26/6, 2020 at 22:44 Comment(0)
L
1

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

Larry answered 3/9, 2020 at 0:37 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.