javascriptreactjsnode.jswebpack

Can't resolve 'querystring' in old dependency


I have an old React app which I'm trying to run locally, but I encountered some issues regarding querystring and pollyfils. Here are my files:

packages.json:

{
  "name": "test-immersion",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "immersion-presentation": "^1.0.0",
    "react": "^16.0.0",
    "react-dom": "^16.0.0",
    "react-scripts": "5.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "pdf": "node ./node_modules/immersion-presentation/make-pdf"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

src/index.js:

import React from "react";
import ReactDOM from "react-dom";
import Presentation from "./Presentation";

// Not using React.StrictMode because of performance issues (renders everything twice)
const rootEl = document.getElementById("root");
ReactDOM.render(<Presentation />, rootEl);

if (module.hot) {
  module.hot.accept("./Presentation", () => {
    const NextPresentation = require("./Presentation").default;
    ReactDOM.render(<NextPresentation />, rootEl);
  });
}

src/Presentation.js:

import React from "react";

/* eslint-disable jsx-a11y/iframe-has-title, jsx-a11y/alt-text,  */
/* eslint-disable no-unused-vars */

import { useState } from "react";

import {
  AnimateSVG,
  Morph,
  m,
  M,
  Show,
  Notes,
  Portal,
  timeline,
  range,
  themes,
} from "immersion-presentation";

import "immersion-presentation/dist/index.css";
import step from "immersion-presentation/dist/step.macro.js";

const {
  Presentation,
  Slide,
  BibliographySlide,
  TitleSlide,
  TableOfContentsSlide,
  SectionSlide,
  QuestionSlide,
  ConclusionSlide,
  Figure,
  List,
  Item,
  Cite,
  Box,
  Qed,
} = themes.modern;

function App() {
  return (
    <Presentation>
      <Slide steps={[1, 2, 3]}>The step is {step}!</Slide>
    </Presentation>
  );
}

export default App;

I'm using node version 16.20.2 and npm version 8.1.0, and when I run yarn start it throws the following error:

ERROR in ./node_modules/wikibase-sdk/lib/utils/build_url.js 7:19-51 Module not found: Error: Can't resolve 'querystring' in 'C:\Users\User\dev\test-immersion\node_modules\wikibase-sdk\lib\utils'

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: { "querystring": require.resolve("querystring-es3") }'
  • install 'querystring-es3' If you don't want to include a polyfill, you can use an empty module like this: resolve.fallback: { "querystring": false }

I see the answer here with webpack, but in my project I do not use webpack, and not sure if this even helps given that the issue comes from a dependency of a dependency. Any ideas how to resolve this?


Solution

  • Considering that the only problem is querystring that is used by wikibase-sdk, the quick fix would be to install querystring-es3 polyfill and map querystring to it, as it has been already suggested in the question.

    The project uses CRA (react-scripts), which uses Webpack internally. It's possible to modify Webpack config through third-party tools like Craco. The project needs to replace all occurrences of react-scripts in scripts with craco and provide custom config in craco.config.js:

    module.exports = {
      webpack: {
        configure: (webpackConfig, { env, paths }) => {
          webpackConfig.resolve.fallback = { querystring: require.resolve("querystring-es3") }
          return webpackConfig;
        },
      },
    };
    

    The necessity to use Node.js polyfills in a browser build often indicates that things already went wrong, because the project uses a module that was intended to be used only in Node and not in a browser.

    This was suggested by previously published error that mentioned fs and module. This may require to polyfill an unspecified amount of Node built-in modules and then the module will refuse to work because most of the polyfills are just stubs; a browser can't do what Node does.

    This requires to isolate a dependency that causes this. In this case the nested dependency is:

    immersion-presentation > citation-js > @citation-js/plugin-wikidata > wikidata-sdk > wikibase-sdk
    

    This is a mistake on the behalf of the authors of the dependencies. wikibase-sdk@7 that the dependency graph is hardcoded to use is forced to use Node built-in modules despite it's built as UMD module that could run in a browser.

    This can be fixed by overriding a troublesome dependency, considering that an override is compatible. The unnecessary dependency on Node builtins was presumably fixed in wikibase-sdk@8. An override can be provided in package.json, the dependencies need to be completely reinstalled:

      "overrides": {
        "citation-js": {
          "@citation-js/plugin-wikidata": {
            "wikidata-sdk": {
              "wikibase-sdk": "8"
            }
          }
        }
      }