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

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?

67526 次浏览

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;

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"
}

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

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