JS周りのリハビリ備忘録(React + Babel + Webpack4)
今さらながらReact 入門しようと思ったんですが、JS 界隈の時代の流れに完全に置いてけぼりになっていました。
https://html5hive.org/react-tutorial/
↑ これをやってるときに、「あれ、browser-sync で表示されるのはいいとして、普通にfile open で開けないんだっけ?」と思っていろいろ調べたのがきっかけです。
※ ちなみに、browser-sync
$ browser-sync start --server . --files "**/*"
単純にfile open すると、ブラウザのconsole に下記エラーが表示されていました。
Access to XMLHttpRequest at 'file://~/tmp/my_react_tutorial2/main.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https. transform.load @ browser.min.js:4
次に、
<script type="text/babel" src="main.js"></script>
の type="text/babel"
を消してみたところ、
Uncaught SyntaxError: Unexpected token <
というエラーが出ました。以下、リハビリのためにこのあたりを解消していった備忘録です。
サマリ
環境
$ sw_vers ProductName: Mac OS X ProductVersion: 10.13.6 BuildVersion: 17G4015 $ npm -v 5.6.0 $ npx -v 9.7.1
before (ファイル構成)
$ tree . . ├── assets │ ├── css │ │ └── base.css │ └── images │ └── home.jpg ├── index.html └── main.js 3 directories, 4 files
作業
$ npm i -D babel-core babel-loader@7 babel-preset-react webpack webpack-cli
$ npm list --depth=0
webpack-demo@1.0.0 ~/tmp/my_react_tutorial2
├── babel-core@6.26.3
├── babel-loader@7.1.5
├── babel-preset-react@6.24.1
├── lodash@4.17.11
├── webpack@4.28.2
└── webpack-cli@3.1.2
$ cat webpack.config.js module.exports = { module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: ['react'] } } ], } ] } };
$ cat package.json { "name": "webpack-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-react": "^6.24.1", "webpack": "^4.28.2", "webpack-cli": "^3.1.2" } }
$ mkdir src $ mv main.js src/index.js
$ tree . -L 1 . ├── babel.config.js ├── dist ├── node_modules ├── package-lock.json ├── package.json ├── src └── webpack.config.js 3 directories, 4 files $ tree dist/ src dist/ ├── assets │ ├── css │ │ └── base.css │ └── images │ └── home.jpg ├── index.html └── main.js src └── index.js 3 directories, 5 files $ tree dist/ src/ dist/ ├── assets │ ├── css │ │ └── base.css │ └── images │ └── home.jpg ├── index.html └── main.js src/ └── index.js 3 directories, 5 files
webpack した結果をfile open
$ npx webpack
$ mv index.html assets/ dist/
$ open dist/index.html
これで、browser-sync したときと同じように表示されました。
以下、調査メモ
まず、手動でbabel でtranspile するとどうなるのか。
$ npm install --save-dev @babel/core @babel/cli @babel/preset-env $ npm install --save @babel/polyfill $ cat babel.config.js const presets = [ [ "@babel/env", { targets: { edge: "17", firefox: "60", chrome: "67", safari: "11.1", }, useBuiltIns: "usage", }, ], ]; module.exports = { presets };
$ ./node_modules/.bin/babel src --out-dir lib { SyntaxError: ~/tmp/my_react_tutorial2/src/main.js: Unexpected token (114:8) 112 | //as a prop, so we need a separate id for that. 113 | return ( > 114 | <Home | ^ 115 | key={index} 116 | id={index} 117 | onToggleSave={toggleSave} at Parser.raise (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:4051:15) at Parser.unexpected (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5382:16) at Parser.parseExprAtom (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6541:20) at Parser.parseExprSubscripts (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6104:21) at Parser.parseMaybeUnary (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6083:21) at Parser.parseExprOps (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5968:21) at Parser.parseMaybeConditional (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5940:21) at Parser.parseMaybeAssign (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5887:21) at Parser.parseParenAndDistinguishExpression (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6699:28) at Parser.parseExprAtom (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6473:21) pos: 3211, loc: Position { line: 114, column: 8 }, code: 'BABEL_PARSE_ERROR' }
JSX がパースできていないらしい。
React はJSX と使うのがいいよ って話でBabel が入っていると認識していたのに、Babel だけではJSX はパースできないとは何事だ。
を見ていると、webpack の話が出てくる。
そもそもBabel って何者だっけ、とかwebpack とはどういう関係だっけ、とか思い始めて調べ始める。
↑ このあたりはとても参考になりました。
とりあえず概念を掴んだので、webpack からbabel を呼ぼうとする
$ npm run webpack npm ERR! missing script: webpack npm ERR! A complete log of this run can be found in: npm ERR! ~/.npm/_logs/2018-12-26T07_26_02_958Z-debug.log
ちょっとよくわからない。あ、local にインストールしたからか、と思って --development をつけても一緒。
ここらへんをみると、どうやら最近は npx
を使うようになっているらしいのでやってみる
$ npx webpack Insufficient number of arguments or no entry found. Alternatively, run 'webpack(-cli) --help' for usage info. Hash: b98875b6bc270ed9ed54 Version: webpack 4.28.2 Time: 64ms Built at: 2018/12/26 16:30:09 WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in Entry module not found: Error: Can't resolve './src' in '~/tmp/my_react_tutorial2'
src ディレクトリは存在しているのに。。
を見ると、設定ファイルが無いとデフォルトで src/index.js を見に行く、とあるので、rename してみる
$ mv src/main.js src/index.js $ npx webpack Hash: 2887e62448d27ce303a0 Version: webpack 4.28.2 Time: 285ms Built at: 2018/12/26 16:34:37 1 asset Entrypoint main = main.js [0] ./src/index.js 268 bytes {0} [built] [failed] [1 error] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in ./src/index.js 114:8 Module parse failed: Unexpected token (114:8) You may need an appropriate loader to handle this file type. | //as a prop, so we need a separate id for that. | return ( > <Home | key={index} | id={index}
元のエラーに戻った。
これでwebpack が使えるようになったっぽいので、元のStackoverflow に戻って、 見よう見まねでwebpack のconfig を書いて実行してみる
$ cat webpack.config.js { test: /\.jsx?$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: ['react'] } } ], } $ npx webpack Unexpected token :
シンタックスエラー。リハビリ中なのでしょうがない。正しくは、上記を rules
に入れなきゃいけなかった (Stackoverflow にも書いてあった)
module.exports = { module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: ['react'] } } ] } ] } };
$ npx webpack Insufficient number of arguments or no entry found. Alternatively, run 'webpack(-cli) --help' for usage info. Hash: 53a7a87a9524e153353b Version: webpack 4.28.2 Time: 49ms Built at: 2018/12/26 16:41:37 WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in Entry module not found: Error: Can't resolve 'babel-loader' in '~/tmp/my_react_tutorial2'
足りないモジュールを入れてリトライ
$ npm install babel-core babel-loader --save-dev npm WARN babel-loader@8.0.4 requires a peer of @babel/core@^7.0.0 but none is installed. You must install peer dependencies yourself. npm WARN babel-loader@8.0.4 requires a peer of webpack@>=2 but none is installed. You must install peer dependencies yourself. npm WARN my-awesome-package@1.0.0 No description npm WARN my-awesome-package@1.0.0 No repository field. npm WARN my-awesome-package@1.0.0 No license field. + babel-loader@8.0.4 + babel-core@6.26.3 added 48 packages, removed 520 packages and moved 5 packages in 8.407s $ npx webpack One CLI for webpack must be installed. These are recommended choices, delivered as separate packages: - webpack-cli (https://github.com/webpack/webpack-cli) The original webpack full-featured CLI. We will use "npm" to install the CLI via "npm install -D". Do you want to install 'webpack-cli' (yes/no): yes Installing 'webpack-cli' (running 'npm install -D webpack-cli')... npm WARN babel-loader@8.0.4 requires a peer of @babel/core@^7.0.0 but none is installed. You must install peer dependencies yourself. npm WARN babel-loader@8.0.4 requires a peer of webpack@>=2 but none is installed. You must install peer dependencies yourself. npm WARN webpack-cli@3.1.2 requires a peer of webpack@^4.x.x but none is installed. You must install peer dependencies yourself. npm WARN my-awesome-package@1.0.0 No description npm WARN my-awesome-package@1.0.0 No repository field. npm WARN my-awesome-package@1.0.0 No license field. + webpack-cli@3.1.2 added 76 packages in 2.462s { Error: Cannot find module 'webpack-cli' at Function.Module._resolveFilename (module.js:555:15) at Function.Module._load (module.js:482:25) at Module.require (module.js:604:17) at require (internal/module.js:11:18) at runCommand.then (~/.npm/_npx/45193/lib/node_modules/webpack/bin/webpack.js:142:5) at <anonymous> at process._tickCallback (internal/process/next_tick.js:160:7) code: 'MODULE_NOT_FOUND' } $ npm list | grep webpack-cli └─┬ webpack-cli@3.1.2 npm ERR! peer dep missing: @babel/core@^7.0.0, required by babel-loader@8.0.4 npm ERR! peer dep missing: webpack@>=2, required by babel-loader@8.0.4 npm ERR! peer dep missing: webpack@^4.x.x, required by webpack-cli@3.1.2
webpack-cli は入ってるはずなのに、存在しないからインストールしようとしてくる。yes と答えてインストールしても、また同じエラーになる。
どういうことなの。。。
$ which npx /usr/local/bin/npx $ ll /usr/local/bin/npx lrwxr-xr-x 1 admin 46 12 17 2017 /usr/local/bin/npx@ -> /usr/local/lib/node_modules/npm/bin/npx-cli.js $ ll /usr/local/lib/node_modules/ | grep webpack $
どうやらnpm のlocal にうまくwebpack が入っていなかったようで、もう一度npm i -D webpack
する
$ ll node_modules/ | grep webpack drwxr-xr-x 7 staff 224 12 26 16:48 webpack-cli/ #=> webpackが入ってない!
$ npx webpack Hash: c0af3a1e4f45824bccdb Version: webpack 4.28.2 Time: 263ms Built at: 2018/12/26 16:55:19 1 asset Entrypoint main = main.js [0] ./src/index.js 2.81 KiB {0} [built] [failed] [1 error] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in ./src/index.js Module build failed (from ./node_modules/babel-loader/lib/index.js): Error: Cannot find module '@babel/core' babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'. at Function.Module._resolveFilename (module.js:555:15) at Function.Module._load (module.js:482:25) at Module.require (module.js:604:17) at require (~/tmp/my_react_tutorial2/node_modules/v8-compile-cache/v8-compile-cache.js:159:20) at Object.<anonymous> (~/tmp/my_react_tutorial2/node_modules/babel-loader/lib/index.js:10:11) at Module._compile (~/tmp/my_react_tutorial2/node_modules/v8-compile-cache/v8-compile-cache.js:178:30) at Object.Module._extensions..js (module.js:671:10) at Module.load (module.js:573:32) at tryModuleLoad (module.js:513:12) at Function.Module._load (module.js:505:3) at Module.require (module.js:604:17) at require (~/tmp/my_react_tutorial2/node_modules/v8-compile-cache/v8-compile-cache.js:159:20) at loadLoader (~/tmp/my_react_tutorial2/node_modules/loader-runner/lib/loadLoader.js:13:17) at iteratePitchingLoaders (~/tmp/my_react_tutorial2/node_modules/loader-runner/lib/LoaderRunner.js:169:2) at runLoaders (~/tmp/my_react_tutorial2/node_modules/loader-runner/lib/LoaderRunner.js:362:2) at NormalModule.doBuild (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModule.js:280:3) at NormalModule.build (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModule.js:427:15) at Compilation.buildModule (~/tmp/my_react_tutorial2/node_modules/webpack/lib/Compilation.js:633:10) at moduleFactory.create (~/tmp/my_react_tutorial2/node_modules/webpack/lib/Compilation.js:1019:12) at factory (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:405:6) at hooks.afterResolve.callAsync (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:155:13) at AsyncSeriesWaterfallHook.eval [as callAsync] (eval at create (~/tmp/my_react_tutorial2/node_modules/tapable/lib/HookCodeFactory.js:32:10), <anonymous>:6:1) at AsyncSeriesWaterfallHook.lazyCompileHook (~/tmp/my_react_tutorial2/node_modules/tapable/lib/Hook.js:154:20) at resolver (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:138:29) at process.nextTick (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:342:9) at process._tickCallback (internal/process/next_tick.js:150:11)
バージョン依存があったらしい。勝手に解決してくれないのか。自分で指定して入れる。
npm i -D babel-loader@7
$ npx webpack Hash: 75e3609ad92c952b5d1e Version: webpack 4.28.2 Time: 636ms Built at: 2018/12/26 16:59:07 1 asset Entrypoint main = main.js [0] ./src/index.js 1.59 KiB {0} [built] [failed] [1 error] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in ./src/index.js Module build failed (from ./node_modules/babel-loader/lib/index.js): Error: Couldn't find preset "react" relative to directory "~/tmp/my_react_tutorial/src" at ~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:293:19 at Array.map (<anonymous>) at OptionManager.resolvePresets (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:275:20) at OptionManager.mergePresets (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:264:10) at OptionManager.mergeOptions (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:249:14) at OptionManager.init (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368:12) at File.initOptions (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/index.js:212:65) at new File (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/index.js:135:24) at Pipeline.transform (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/pipeline.js:46:16) at transpile (~/tmp/my_react_tutorial/node_modules/babel-loader/lib/index.js:50:20) at Object.module.exports (~/tmp/my_react_tutorial/node_modules/babel-loader/lib/index.js:173:20)
今度はreact が見つからない、とのことなので、関係ありそうなモジュールを追加
$ npm i -D babel-preset-react
$ npx webpack Hash: 3500f1c5383324b5f4a0 Version: webpack 4.28.2 Time: 412ms Built at: 2018/12/26 17:00:28 Asset Size Chunks Chunk Names main.js 3.22 KiB 0 [emitted] main Entrypoint main = main.js [0] ./src/index.js 3.84 KiB {0} [built] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
これで、ようやく dist 配下に、bundle されたmain.js が置かれた。
時代の流れについていくのは大変だなぁ。
あれ、何しようとしてたんだっけ