Webpack 4 で Cannot assign to read only property 'exports' of object エラー
こんにちは、Webpackでのbundle化初心者です。
今までgulpで分けたJSファイルをconcatして一つにまとめていました。gulpのconcatだとファイル名を01-みたいなprefixをつけて結合順をコントロールしてました。webpackだとその辺りも解決してくれるっぽいので良いですね。(バンドルするための処理が乗るので最終的なコード量は多くなると思いますが
今回自分で分割したファイルを読み込ませていてwebpackでは問題なくbundleされたファイルが生成されたけど、ブラウザで確認すると Cannot assign to read only property 'exports' of objectというエラーが発生したのでメモ。
import と module.exports が混在してるとよろしくないっぽい
モジュールの化はES6のimport / export の組み合わせと、CommonJSのmodule.exports / require があり、これらが混在してしまうのがよろしくないようです。
ただ、require は import / export と使っても問題ないようです。
e.g
import / export と module.exports が混在するとエラー
// config.js module.exports = {name: '佐倉 千代'}; // name.js import config from './config'; module.exports = '野崎 梅太郎'; // app.js import name from './name'; console.log(name);
↓
webpackでbundle.jsは生成されるけどブラウザで確認すると
Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
name.js で importとmodule.exports が混在しているのでNG
import / export の組み合わせにすればOK
// config.js export default {name: '佐倉 千代'}; // name.js import config from './config'; export default config.name; // app.js import name from './name'; console.log(name);
→ 佐倉 千代 問題なく実行される
module.exportsされているものをimport / requireして export するのはエラーにならないっぽい
// '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.
とあったのですが、
試していたWebpack v4 の環境ではmodule.exportsされたものを読み込んでexportしてもエラーにはならなかったので仕様が変わったのかもしれません。
// config.js module.exports = {name: '佐倉 千代'}; // name.js import config from './config'; export default config.name; // app.js import name from './name'; console.log(name);
→ 佐倉 千代
require / module.exports の組み合わせにしてもエラーになる場合がある
require / module.exports の組み合わせにすればOKかと思ったのですが、
requireしたオブジェクトをそのままmodule.exportsしているとエラーになるようです。
require / module.exports で統一されてるけどエラーになるパターン
// config.js module.exports = {name: '野崎 梅太郎'}; // name.js const config = require('./config'); module.exports = config.name; // app.js const name = require('./name'); console.log(name);
→ Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
最終的な部分がimport文でもエラー
// config.js module.exports = {name: '野崎 梅太郎'}; // name.js const config = require('./config'); module.exports = config.name; // app.js import name from './name'; console.log(name);
→ Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
経由しているファイル(name.js)で読み込んでいるモジュールがexportだろうと、読み込んだ内容をmodule.exportsしていればエラー
// config.js export {name: '野崎 梅太郎'}; // name.js const config = require('./config'); module.exports = config.name; // app.js const name = require('./name'); console.log(name);
→ Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
// config.js export {name: '野崎 梅太郎'}; // name.js const config = require('./config'); module.exports = config.name; // app.js import name from './name'; console.log(name);
→ Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
module.exports が読み込んだ内容でなければOK
// config.js module.exports = {name: '野崎 梅太郎'}; // name.js const config = require('./config'); module.exports = '御子柴 実琴'; // app.js import name from './name'; console.log(name);
→ 御子柴 実琴 問題なく実行される
※ config.js が exportでも、app.jsはrequireでもエラーにはならない
require はimport / exportと混在しても常にOK
// config.js module.exports = {name: '堀 政行'}; // name.js const config = require('./config'); export default config.name; // app.js import name from './name'; console.log(name);
→ 堀 政行 問題なく実行される
requireとimportが同じファイル内で混在していても問題ないので次のような感じでも問題なくbundleされて実行もできる
// config.js module.exports = {name: '堀 政行'}; // name.js cost $ = require('jquery'); import config from './config'; config.name = '鹿島 遊'; export default config.name; // app.js const config = require('./config'); import name from './name'; console.log(name);
→ 鹿島 遊
問題なく実行されるけど、importとrequireが同じファイルで混在してると見通しよくないので良くないって感想。
まとめ
- ファイル内がエラーにならない組み合わせであればOK
import / exportかimport / require / exportの組み合わせで統一するrequire / module.exportsでの組み合わせで統一する- 但し
requireで読み込んだオブジェクトをmodule.exportsするとエラー
- 但し
importとmodule.exportsが同じファイルにあるとエラー
module.exportsはトラップが多いので使わないほうが良さそう。という感想です。
と、THE Webpack 初心者って感じのエラーでした。
EMS (ES module)の世界と CJS (CommonJS)の世界があって混在してるって訳ですね。
JavaScriptもブラウザのECMAScriptとnode.jsの世界があって仕様がそれぞれ違うってのと近い感じでしょうか…
昨今のJS界隈の流れとか歴史とか含めた仕様の違い理解してないとハマりポイントっぽいって印象です。 (IEのJScriptに悩まされる事は減ったけど
絶対王者jQueryが倒れ、世は将にJSフロントエンド戦国時代!
(ここ数年は勢力図が落ち着いてる感じっぽいですが、その感離れていたのでキャッチアップが大変です
[参考]
- Cannot assign to read only property 'exports' of object '#<Object>' (mix require and export) · Issue #4039 · webpack/webpack · GitHub
- Babel 7 Upgrading creating error: “TypeError: Cannot assign to read only property 'exports' of object '#<Object>'” | stackoverflow
- babel+webpackな環境でESM(import構文)とCJS(module.exports構文)を混在させると『Cannot assign to read only property ‘exports’ of object ‘#<Object>’』と怒られる - ひと夏の技術
import / exports と module.exports / require について
- Node.js: exports vs module.exports
- Node.js : exports と module.exports の違い(解説編) - ぼちぼち日記
- ES6のexportについて詳しく調べた - Qiita
違いを理解していないので、いずれ...
最近作業中 月刊少女野崎くん 見てました。面白いよね
