kshetline
kshetline

Reputation: 13682

How can I get webpack to shut up about "Critical dependency: require function is used in a way..."?

Getting rid of this warning is my immediate desire. Perhaps, however, what is needed is an entirely different strategy altogether.

I've created an npm library for various date/time functions. Nearly all of the code is general-purpose code that works for Node.js or a web browser. There's one feature, however, that requires some optional, platform-specific code: polling a remote URL for timezone updates. (The rules for when Daylight Saving Time begins and ends are constantly in flux around the world).

With a bit of ugly hacking I can make my code compile for both web and for Node, but there's still a webpack warning:

Critical dependency: require function is used in a way in which dependencies cannot be statically extracted

Different variations I've tried simply result in different warnings related to require, or the code being required.

I can suppress the warnings using FilterWarningsPlugin, but that only suppresses the warnings for me when I build my npm package. Any consumer of my package will still get this warning. If they suppress the warning using FilterWarningsPlugin, then any consumer of their code could still get the warning, etc.

Here's what I've got now:

import { IZonePoller } from './i-zone-poller';

// The following crazy mess allows @tubular/time to be compiled for Node.js or for browsers
// without pulling in Node.js dependencies.
//
// eslint-disable-next-line no-new-func
const requestText: (url: string) => Promise<any> = new Function('require', `
  try {
    return require('by-request').requestText;
  }
  catch {
    return null;
  }
`)(require);

export const zonePollerNode: IZonePoller = {
  async getTimezones(url: string): Promise<{ [p: string]: string }> {
    if (!requestText) {
      const msg = 'npm package "by-request" should be installed to use zonePollerNode';
      console.error(msg);
      throw new Error(msg);
    }

    const zones = (await requestText(url)).replace(/^.*?=\s*/, '');

    return JSON.parse(zones);
  }
};

I've tried (and failed) to find some sneaky way to refer to require that webpack will ignore. webpack's "magic comments" don't appear to offer anything that will help.

One possible solution is to make an entirely separate npm package out of this one little bit of functionality, but that seems like overkill, and an awful reason to create a separate package.

Any brilliant ideas out there? Is there a platform-neutral technique, that won't cause browser CORS issues, to fetch web resources that I could use for Node and browsers?

Upvotes: 1

Views: 4038

Answers (2)

freya
freya

Reputation: 479

Setting module.unknownContextCritical = false in Webpack config helped in my case.

There’s also exprContextCritical for if you run into the related “Critical dependency: the request of a dependency is an expression” warning.

Upvotes: 2

kshetline
kshetline

Reputation: 13682

This approach using dynamic import finally worked for me:

import { IZonePoller } from './i-zone-poller';

let requestText: (url: string) => Promise<string>;

export const zonePollerNode: IZonePoller = {
  async getTimezones(url: string): Promise<{ [p: string]: string }> {
    if (!requestText) {
      try {
        // @ts-ignore
        requestText = (await import(/* webpackIgnore: true */ 'by-request')).requestText;
      }
      catch {}
    }

    if (!requestText) {
      const msg = 'npm package "by-request" should be installed to use zonePollerNode';
      console.error(msg);
      throw new Error(msg);
    }

    const zones = (await requestText(url)).replace(/^.*?=\s*/, '');

    return JSON.parse(zones);
  }
};

I didn't think this would work because the very first time I typed in import(... my editor quickly place a squiggly red line below the import, with this error message:

TS1323: Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'esnext', 'commonjs', 'amd', 'system', or 'umd'.

I wanted to leave the module format at ES2015, so this didn't seem like an option. And I didn't think import() was going to work when compiled for a browser target either.

But, lo and behold, simply ignoring the error with // @ts-ignore miraculously worked, along with the webpack "magic comment", to solve all of my compilation and warning-suppression desires.

Upvotes: 2

Related Questions