Search code examples
javascriptbabeljscreate-react-apppolyfillscore-js

why does Babel in create-react-app not polyfill Array.prototype.at properly?


I came across an issue and found out its because Babel does not polyfill .at as default

According to spec , Array.prototype.at is merely stage 4, and I already set browserslist to > 0.2%, which includes Chrome 86(not supported .at yet)

Why on the earth Babel doest not polyfill .at ?

Babel Playground


Solution

  • Hey guys I believe I've fond the reason;

    TLDR, the CRA is setting the wrong core-js version config for babel

    I'm using create-react-app to build my web app, where I fond polyfill not work properly after installing core-js and import in entry file, according to officail doc;

    And the key reason is in babel-preset-react-app, the babel config which CRA pre-configured and not easily modifiyed by users, have set 3 for cors-js version; code is here

    Whereas, the Babel doc has strongly recommanded to use minor verision , Or :

    It is recommended to specify the minor version otherwise "3" will be interpreted as "3.0" which may not include polyfills for the latest features.

    And in conculsion, no matter what version of corejs we installed, it always use polyfill rule with 3.0 version , which would make mistake for some feature polyfills, like Array.prototype.at ;

    And there is some method to resolve this problem

    if u are using pure CRA, u have to do this to import all core-js into your bundle result;

    import R from "core-js/stable";
    import React from "react";
    import ReactDOM from "react-dom/client";
    import "./index.css";
    import App from "./App";
    import reportWebVitals from "./reportWebVitals";
    
    // eslint-disable-next-line no-unused-expressions
    R;
    

    if u work with anything like react-app-rewired or craco, u can achieve this by a more elegant way by correcting the babel-preset-react-app code in your node_modules:

    //resolve-core-js.js
    const path = require("path");
    const fs = require("fs");
    
    const setBabelPresetConfig = (version = "3.24") => {
      console.info(
        `[cra-core-js-resolve] try set core-js to ${version} in babel-preset-react-app`
      );
      try {
        const babelRuntimeEntry = require.resolve("babel-preset-react-app");
        const babelPresetCreateAppCreate = path.resolve(
          babelRuntimeEntry,
          "../create.js"
        );
        const content = fs.readFileSync(babelPresetCreateAppCreate, {
          encoding: "utf-8",
        });
        const template = "corejs: 3,";
        if (!content.includes(template)) {
          console.info("[cra-core-js-resolve] template not fond, do nothing");
          return;
        }
        const replacedContent = content.replace(
          "corejs: 3,",
          `corejs: ${version},`
        );
        fs.writeFileSync(babelPresetCreateAppCreate, replacedContent);
    
        if (require.cache["babel-preset-react-app"]) {
          delete require.cache["babel-preset-react-app"];
        }
    
        console.info(`[cra-core-js-resolve] set core-js to ${version}  .done`);
      } catch (e) {
        if (e.code === "MODULE_NOT_FOUND") {
          console.info(
            "[cra-core-js-resolve] babel-preset-react-app not fond, do nothing"
          );
    
          return;
        }
        console.error(
          "[cra-core-js-resolve] error happened when set babel-preset-react-app "
        );
        console.error(e);
      }
    };
    module.exports = setBabelPresetConfig;
    
    //craco.config.js
    const resolveCoreJs = require('/path/to/resolve-core-js.js')
    resolveCoreJs()
    
    module.exports={
    //...
    }