2022年2月

Bug描述

  • react 17 引入 @cosmjs/amino运行,报错如下:

    Compiled with problems:X
    
    ERROR in ./node_modules/cipher-base/index.js 3:16-43
    
    Module not found: Error: Can't resolve 'stream' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/cipher-base'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
    - install 'stream-browserify'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "stream": false }
    
    ERROR in ./node_modules/hash-base/node_modules/safe-buffer/index.js 4:13-30
    
    Module not found: Error: Can't resolve 'buffer' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/hash-base/node_modules/safe-buffer'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
    - install 'buffer'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "buffer": false }
    
    ERROR in ./node_modules/libsodium/dist/modules/libsodium.js 49:23-46
    
    Module not found: Error: Can't resolve 'path' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/libsodium/dist/modules'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
    - install 'path-browserify'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "path": false }
    
    ERROR in ./node_modules/readable-stream/lib/_stream_readable.js 46:13-37
    
    Module not found: Error: Can't resolve 'buffer' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/readable-stream/lib'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
    - install 'buffer'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "buffer": false }
    
    ERROR in ./node_modules/readable-stream/lib/_stream_writable.js 70:13-37
    
    Module not found: Error: Can't resolve 'buffer' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/readable-stream/lib'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
    - install 'buffer'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "buffer": false }
    
    ERROR in ./node_modules/readable-stream/lib/internal/streams/buffer_list.js 74:15-32
    
    Module not found: Error: Can't resolve 'buffer' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/readable-stream/lib/internal/streams'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
    - install 'buffer'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "buffer": false }
    
    ERROR in ./node_modules/ripemd160/index.js 3:13-37
    
    Module not found: Error: Can't resolve 'buffer' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/ripemd160'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
    - install 'buffer'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "buffer": false }
    
    ERROR in ./node_modules/safe-buffer/index.js 2:13-30
    
    Module not found: Error: Can't resolve 'buffer' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/safe-buffer'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
    - install 'buffer'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "buffer": false }
    
    ERROR in ./node_modules/string_decoder/node_modules/safe-buffer/index.js 4:13-30
    
    Module not found: Error: Can't resolve 'buffer' in '/Users/eason/Desktop/untitled folder/react17-cosmjs-conflect2/node_modules/string_decoder/node_modules/safe-buffer'
    
    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.
    
    If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
    - install 'buffer'
    If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "buffer": false }

复现步骤

  1. 新建react项目

    npx create-react-app react17-cosmjs-conflect
  2. 安装@cosmjs/amino

    npm i @cosmjs/amino
  3. 在/src/index.js引入@cosmjs/stargate

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    
    import {decodeBech32Pubkey} from '@cosmjs/amino'
    console.log(decodeBech32Pubkey)
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById('root')
    );
    reportWebVitals();
  4. 运行

    npm run start
  5. 报错如开篇所示

Bug原因

npx create-react-app命令创建的React17默认调用的是webpack5,相比webpack4,精简了很多自带的包,需要手动安装才行

解决方法1

将react-scripts由默认的5.0.0改为4.0.3,npm i react-scripts@4.0.3即可

解决方法2

  1. 将react 17的webpack config从node_modules里暴露出来,如果提示git uncommitted就先commit一下

    npm run eject
  2. 安装这4个包:

    npm i stream-browserify buffer path-browserify crypto-browserify crypto-browserify
  3. 根据报错提示配置 /config/webpack.config.js

    搜索“resolve:”,在resolve内添加fallback节点,内容如下

    fallback: {
      "stream": require.resolve("stream-browserify"),
      "buffer": require.resolve("buffer/"),
      "path": require.resolve("path-browserify"),
      "crypto": require.resolve("crypto-browserify")
    }
  4. 这时候再npm run start运行就没有error了,但是有40个warning,都是关于cosmjs缺少source map的,如果不想看到这个错误,可以在跟resolve节点同级的地方添加以下属性,搜索“resolve:”,在上一行放:

    ignoreWarnings: [/Failed to parse source map/],

提示:如果没有将webpack config从node_modules里暴露出来,直接在项目根目录创建webpack.config.js文件去配置无法解决此bug

create react project

npx create-react-app react-router-test

install react-router-dom@5.3.0

npm i react-router-dom@5.3.0

create file /src/RouterHome.jsx, code:

import { Component } from 'react'
export default class RouterHome extends Component {
  render() {
    return (
      <>
        <p><button onClick={()=>this.props.history.push('login')}>Login</button></p>
      </>
    )
  }
}

create file /src/RouterLogin.jsx, code:

import { Component } from 'react'
export default class RouterLogin extends Component {
  render() {
    return (
      <div>RouterLogin</div>
    )
  }
}

create file /src/RouterNotFound.jsx, code:

import { Component } from 'react'
export default class RouterNotFound extends Component {
  render() {
    return (
      <div>RouterNotFound</div>
    )
  }
}

create file /src/routers/index.js, code:

import { BrowserRouter as Router, Switch, Route, useHistory } from "react-router-dom";
// use react-router-dom@5.3.0
import Home from '../RouterHome'
import Login from '../RouterLogin'
import NotFound from "../RouterNotFound";
const BaseRoute = () => {
  return (
    <Router history={useHistory}>
      <Switch>
        <Route exact path="/" component={Home}></Route>
        <Route exact path="/login" component={Login}></Route>
        <Route component={NotFound}></Route>
      </Switch>
    </Router>
  )
}
export default BaseRoute

edit /src/index.js, code:

import ReactDOM from 'react-dom';
import App from './routers/index';
ReactDOM.render(
  <App />,
  document.getElementById('root')
)

run

npm run start

behaviour

access http://localhost:3000/ and http://localhost:3000/bridge and you will see different text
access url not exits, for example http://localhost:3000/abc, you will see text RouterNotFound

npm i cheerio

const fs = require("fs");
const cheerio = require("cheerio");
fs.readFile("./yt.html", "utf-8", (err, data) => {
  if (err) throw err; // /user/brhm8900/videos
  const $ = cheerio.load(data);
  $("#video-title").each(function () {
    const title = $(this).text();
    if ($(this).text().indexOf("S06E") > -1) {
      console.log($(this).attr("href"));
    }
  });
});

'use strict';
exports.main = async (event, context) => {
    let headers = event.headers
    delete headers.host
    const res = await uniCloud.httpclient.request(event.queryStringParameters.url, {
        method: event.httpMethod,
        headers: headers,
        content: event.body,
        rejectUnauthorized: false,
        followRedirect: true
    })
    return {
        // mpserverlessComposedResponse: true, // https://uniapp.dcloud.io/uniCloud/http?id=integrationresponse
        statusCode: res.status,
        headers: res.headers,
        body: res.data.toString()
    }
}

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Add network to keplr</title>
    <style>
      input {width: 256px; padding: 5px}
      td {padding: 5px}
    </style>
    <link rel="icon" type="image/svg+xml" href="https://docs.keplr.app/favicon-svg.svg">
  </head>
  <body>
    <div style="max-width: 512px; margin: 0 auto; padding-top: 100px">
      <table border="0">
        <tr>
          <td>chainId</td>
          <td><input id="chainId" value="xxxChain"></td>
        </tr>
        <tr>
          <td>rpc</td>
          <td><input id="rpc" value="http://ip:26657"></td>
        </tr>
        <tr>
          <td>rest</td>
          <td><input id="rest" value="http://ip:1317"></td>
        </tr>
        <tr>
          <td>prefix</td>
          <td><input id="prefix" value="jolt"></td>
        </tr>
        <tr>
          <td>coinDenom</td>
          <td><input id="coinDenom" value="VUSD"></td>
        </tr>
        <tr>
          <td>coinDecimals</td>
          <td><input id="coinDecimals" value="0"></td>
        </tr>
        <tr>
          <td>gasPriceStep</td>
          <td><input id="gasPriceStep" value="0.01"></td>
        </tr>
        <tr>
          <td>walletUrlForStaking</td>
          <td><input id="walletUrlForStaking" value="url"></td>
        </tr>
        <tr>
          <td><button onclick="submit()">Submit</button></td>
          <td></td>
        </tr>
      </table>
      <p>Usage: https://docs.keplr.app/api/suggest-chain.html</p>
    </div>
    <script>
      submit = async () => {
        if (!keplr) {
          alert("Please install keplr extension"); return
        }
        const prefix = document.getElementById('prefix').value
        const coinDenom = document.getElementById('coinDenom').value
        const coinDecimals = parseInt(document.getElementById('coinDecimals').value)
        const gasPriceStep = Number(document.getElementById('gasPriceStep').value)
        await keplr.experimentalSuggestChain({
          chainId: document.getElementById('chainId').value,
          chainName: document.getElementById('chainId').value,
          rpc: document.getElementById('rpc').value,
          rest: document.getElementById('rest').value,
          bip44: {
            coinType: 118,
          },
          bech32Config: {
            bech32PrefixAccAddr: prefix,
            bech32PrefixAccPub: prefix + "pub",
            bech32PrefixValAddr: prefix + "valoper",
            bech32PrefixValPub: prefix + "valoperpub",
            bech32PrefixConsAddr: prefix + "valcons",
            bech32PrefixConsPub: prefix + "valconspub",
          },
          currencies: [
            { 
              coinDenom: coinDenom,
              coinMinimalDenom: coinDenom,
              coinDecimals: coinDecimals
            }, 
          ],
          feeCurrencies: [
            {
              coinDenom: coinDenom, 
              coinMinimalDenom: coinDenom,
              coinDecimals: coinDecimals
            },
          ],
          stakeCurrency: {
            coinDenom: coinDenom,
            coinMinimalDenom: coinDenom,
            coinDecimals: coinDecimals
          },
          coinType: 118,
          gasPriceStep: { 
            low: gasPriceStep,
            average: gasPriceStep*2,
            high: gasPriceStep*3,
          },
          walletUrlForStaking: document.getElementById('walletUrlForStaking').value
        })
      }
    </script>
  </body>
</html>