かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

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というエラーが発生したのでメモ。

importmodule.exports が混在してるとよろしくないっぽい

モジュールの化はES6のimport / export の組み合わせと、CommonJSのmodule.exports / require があり、これらが混在してしまうのがよろしくないようです。 ただ、requireimport / export と使っても問題ないようです。

e.g

import / exportmodule.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.jsimportmodule.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 するのはエラーにならないっぽい

Cannot assign to read only property 'exports' of object '#<Object>' (mix require and export) · Issue #4039 · webpack/webpack · GitHub こちらには

// '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かと思ったのですが、
import, 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.jsexportでも、app.jsrequireでもエラーにはならない

requireimport / 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);

堀 政行 問題なく実行される

requireimportが同じファイル内で混在していても問題ないので次のような感じでも問題なく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);

鹿島 遊
問題なく実行されるけど、importrequireが同じファイルで混在してると見通しよくないので良くないって感想。

まとめ

  • ファイル内がエラーにならない組み合わせであればOK
  • import / exportimport / require / export の組み合わせで統一する
  • require / module.exports での組み合わせで統一する
    • 但しrequireで読み込んだオブジェクトをmodule.exportsするとエラー
  • importmodule.exportsが同じファイルにあるとエラー

module.exportsはトラップが多いので使わないほうが良さそう。という感想です。

と、THE Webpack 初心者って感じのエラーでした。
EMS (ES module)の世界と CJS (CommonJS)の世界があって混在してるって訳ですね。
JavaScriptもブラウザのECMAScriptとnode.jsの世界があって仕様がそれぞれ違うってのと近い感じでしょうか…
昨今のJS界隈の流れとか歴史とか含めた仕様の違い理解してないとハマりポイントっぽいって印象です。 (IEJScriptに悩まされる事は減ったけど
絶対王者jQueryが倒れ、世は将にJSフロントエンド戦国時代!
(ここ数年は勢力図が落ち着いてる感じっぽいですが、その感離れていたのでキャッチアップが大変です


[参考]

import / exportsmodule.exports / require について

違いを理解していないので、いずれ...

 

月刊少女野崎くん ネタラバ 法令遵守 野崎梅太郎

月刊少女野崎くん ネタラバ 法令遵守 野崎梅太郎

最近作業中 月刊少女野崎くん 見てました。面白いよね