+++ date = "2018-08-25" tags = ["webpack","babel"] title = "exchatのfrontendをやってみる" slug = "webpack" +++ ## 導入 昨日、[tony612/exchat](https://github.com/tony612/exchat)を扱いましたが、今回はexchatのfrontendの内容になります。 heorkuでは、通常、/app, /webにわけられdeployされます。 なので、phxのようなbackendの成否とreactのようなfrontendの成否は基本的に別々になっています。 つまり、phxのdeployは正常に成功したけど、webpackが失敗して、/webがうまく表示できないなんてこともありえます。 exchatは、backendをelixir(phoenix)で構成し、frontendは、react, reduxなどで構成しているようです。 ここで、frontend(js)のpreview, build, convertなどには、webpack, babelなどを使用しているようです。 今回は、exchatのfrontendの方を少しだけ見ていこうかなと思います。 ## webpack buildされたものは、`./priv/static/app.js`に置かれるようですね。しかし、.gitignoreに書かれていますので、pushしません。 ```sh $ cat webpack.config.js $ mix deps.get $ npm i $ webpack $ cat ./priv/static/app.js $ mix phx.server ``` layoutを変えるには、`./web/templates/layout`から`html:head`より上をいじることもできますが、基本的には、`./client/`以下を触ります。 `./config/dev.exs`を見ることで、どのようにpreviewされるのかわかります。 riotを触ってた頃、たまたまwebpackも使ってた気がするんですけど、もう忘れてる。 なお、cloudflareとかを利用してる場合、cacheなどが効いてるので、今回生成されるようなapp.jsみたいな一箇所に詰め込まれたファイルというのは、特に、なかなか反映されないことがあります。これは、purgeをするとか、もしくはherokuapp.comで確認するとかしたほうがいいわけですが、それでも反映するまでにしばらく時間がかかることが多いです。browserのprivate windowを利用しても同じ。frontendはこのあたり、大変ですね。あくまで初見の印象ですが。 previewは、`$ bash ./compile`したあとに、`$ mix phx.server`します。`localhost:4000` > You are currently using minified code outside of NODE_ENV === 'production'. This means that you are running a slower development build of Redux. You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) to ensure you have the correct code for your production build. ## react > Warning: It looks like you're using a minified copy of the development build of React. When deploying React apps to production, make sure to use the production build which skips development warnings and is faster. See https://fb.me/react-minification for more details. ```sh $ cat ./compile info "Building Phoenix static assets" NODE_ENV=production webpack -p mix phoenix.digest $ bash ./compile ``` どうやら、buildは、`NODE_ENV=production webpack -p`すればいいらしいですね。 ## heroku ./clientを書き換えて、heroku pushしても、全く変更が反映されずにおかしいなと思っていましたが、heroku cacheが効いていたのかもしれません。 `./priv/static/js/app.js`を`./client/js/app.js`に置き換えることで何故か対応できましたが、errorが色々出るので。 ```sh $ heroku plugins:install heroku-repo $ heroku repo:purge_cache ``` ## update 色々なパッケージをupdateしていきます。 ```sh $ npm list --depth=0 | grep webpack # package.json update $ npm install -g npm-check-updates $ ncu $ ncu -u $ npm update ``` webpackを`1 -> 4`にしたので、だいぶ手こずりました。 一応、成果物を上げておきます。 追記 : 動くようになりました。`react-router@2.x`を指定すると動くのですが、`3.x ~ 4.x`では正常に動きません。多分、versionの書き方が必要なのでしょう。特に、prop-typesあたりのコードです。ただし、react 16.xにupdateすると、react-router 2.xには`_react.PropType`で書かれた箇所があります。`node_modules/react-router/lib/PropType.js`なのですが、現在のsrcでreactをupdateしたい場合は、それを書き換えるしかないですね。 forkしてgithubに上げると、こんな感じで取ってこれます。ただし、webpackで反映されないんで、一旦、npm iしたあとに、再度、本家のversionを指定して、npm iすると、なぜかforkしたlibが読み込まれました。 > package.json ```sh "react-router" : "git:https://github.com/syui/react-router#v2.8.2" ``` 追記終わり。 では、updateした設定ファイルを見ていきます。 > .babelrc ```json { "presets": ["react", "es2015", "stage-2"], "plugins": [ "transform-function-bind" ] } ``` > package.json ```json { "name": "exchat", "version": "2.0.0", "description": "phx chat", "main": "index.js", "directories": { "test": "test" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "syui", "license": "MIT", "repository": {}, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.0.0", "babel-plugin-transform-function-bind": "^6.22.0", "babel-preset-es2015": "^6.5.0", "babel-preset-react": "^6.5.0", "babel-preset-stage-2": "^6.5.0", "webpack": "^4.17.1", "webpack-cli": "^3.1.0" }, "dependencies": { "css-loader": "^1.0.0", "sass-loader": "^7.1.0", "resolve-url-loader": "^2.3.0", "style-loader": "^0.23.0", "url-loader": "^1.1.1", "file-loader": "^2.0.0", "bootstrap-sass": "^3.3.6", "gravatar": "^1.5.2", "history": "^4.7.2", "humps": "^2.0.1", "isomorphic-fetch": "^2.2.1", "jquery": "^3.3.1", "jwt-decode": "^2.0.1", "lodash": "^4.11.2", "normalizr": "^2.3.0", "phoenix": "file:./deps/phoenix", "phoenix_html": "file:./deps/phoenix_html", "prop-types": "^15.6.2", "react-bootstrap": "^0.32.3", "react-select": "^2.0.0", "react-router-redux": "^4.0.8", "redux": "^4.0.0", "redux-thunk": "^2.0.1", "redux-logger": "^3.0.6", "react-redux": "^5.0.7", "react-router": "^2.8.1", "react": "^15.5", "react-dom": "^15.5" } } ``` > webpack.config.js ```js var path = require('path'); var webpack = require('webpack'); var config = { mode: 'production', entry: [ path.resolve(__dirname, './client/js/app.js') ], output: { path: path.resolve(__dirname, './priv/static/js'), filename: 'app.js', publicPath: '/', }, module: { rules: [ { test: /\.jsx?$/, use: { loader : 'babel-loader', } }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', mimetype: 'application/font-woff' } }] }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', mimetype: 'application/font-woff' } }] }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', mimetype: 'application/octet-stream' } }] }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: 'file-loader' }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', limit: '10000', mimetype: 'image/svg+xml' } }] }, { test: /\.scss$/, loader: 'style-loader!css-loader!sass-loader' }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] }, devServer: { contentBase: path.resolve(__dirname, './client'), inline: true, open: true }, resolve: { extensions: ['.js', '.jsx', '.scss', '.css'] }, plugins: [ new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery' }) ] } module.exports = config ``` ```sh $ mix deps.get $ npm i $ cat ./compile ./node_modules/webpack-cli/bin/cli.js -p $ bash ./compile $ mix phx.server ``` あとは、以下の箇所のコードを修正しなければいけません。 ### react-select > Module not found: Error: Can't resolve 'react-select/dist/react-select.css' jsで読み込まれるので最新では必要なくなったらしいです。 > ./client/js/app.js ```js - import 'react-select/dist/react-select.css' ``` ```sh $ mix deps.update postgrex $ bash ./compile $ mix phx.server ``` ### prop-types > React.PropTypes.func : Uncaught ReferenceError: Component is not defined https://github.com/brigand/babel-plugin-flow-react-proptypes/issues/187 ```js - import React, { PropTypes, Component } from 'react' + import React, { Component } from 'react' + import PropTypes from "prop-types"; Settings.propTypes = { - dispatch: React.PropTypes.func + dispatch: PropTypes.func } ``` こんな感じで直していけばいいです。ファイルは、`grep -R PropType . | sort | uniq`とかしましょう。 `react-codemod`を使って修正できたりもするようです。しかし、今回のsrcは、skipでした。 > Warning: Accessing PropTypes via the main React package is deprecated, and will be removed in React v16.0. Use the latest available v15.x prop-types package from npm instead. For info on usage, compatibility, migration and more, see https://fb.me/prop-types-docs ```sh $ npm install -g jscodeshift $ git clone https://github.com/reactjs/react-codemod.git $ echo `pwd`/react-codemod/transforms/React-PropTypes-to-prop-types.js | pbcopy $ find ./client/js -name "*.js" | xargs jscodeshift -t "`pbpaste`" 28 ok Time elapsed: 0.000seconds # skipされるなら以下のコマンド $ find ./client/js -name "*.js" | xargs jscodeshift --extensions jsx -t "/Users/syui/git/exchat/react-codemod/transforms/React-PropTypes-to-prop-types.js" $ npm i react react-dom prop-types ``` ### normalizr > Uncaught TypeError: normalizr.Schema is not a constructor これはバージョン下げた。 ### redux > Uncaught TypeError: (0 , reduxLogger.createLogger) is not a function > redux-logger : ^2.x -> ^3.x ```js - import createLogger from 'redux-logger' + import { createLogger } from 'redux-logger' ``` > TypeError: Cannot read property 'listen' of undefined > unsubscribeFromHistory = history.listen(handleLocationChange); ```sh "react-router": "^3.0.5", "react-router-redux": "^4.0.8", ``` ただし、`react-router@2.x`でないと`npm i -S prop-types`に対応したcodeはうまく動作しない。 ### react-dom, redux-logger > Uncaught RangeError: Maximum call stack size exceeded https://github.com/erikras/redux-form/issues/2629 ### webpack webpackのversionをいろいろいじってると、config/dev.exsやcompileのwebpackを`./node_modules/webpack/bin/webpack.js`から`./node_modules/webpack-cli/bin/cli.js`などに変更しなければならないことがあります。もちろん、環境変数でやってもいいですが。 ## 感想 node packageのupdateとかやりましたが、webpackのbuildするのは成功しましたが、やはり./client以下のreactで書かれたcodeが古いので、そちらを書き換えないと表示されない感じでした。残念です。特に、`prop-types`あたりがめんどくさそうでした。 追記 : いけました。ただし、`react-router@2.x`でなければならないのと、`react@15.x -> react@16.x`にしたい場合は、`react-router@2.x`のlib/PropTypesを書き換えなければならない感じでした。