Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundling typescript using webpack: the request of a dependency is an expression (+possible fix) #39436

Open
AviVahl opened this issue Jul 6, 2020 · 22 comments
Labels
Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Milestone

Comments

@AviVahl
Copy link

AviVahl commented Jul 6, 2020

TypeScript Version: 3.9.6 and 4.0.0-dev.20200706

Search Terms:
webpack request of a dependency expression

Code

import ts from 'typescript';
console.log(ts)

Expected behavior:
Bundle successfully, and without warnings, using webpack.

Actual behavior:
Bundles successfully, but a warning is shown:

WARNING in ./node_modules/typescript/lib/typescript.js 5710:41-60
Critical dependency: the request of a dependency is an expression

Playground Link:
N/A

Related Issues:
Couldn't find any that talked about this issue

Suggested fix:

The warning is printed due to the following dynamic require call:

require: function (baseDir, moduleName) {
    try {
        var modulePath = ts.resolveJSModule(moduleName, baseDir, nodeSystem);
        return { module: require(modulePath), modulePath: modulePath, error: undefined };
    }
    catch (error) {
        return { module: undefined, modulePath: undefined, error: error };
    }
}

Rather than calling require directly, create a helper function:

function requireModule(requestingModule, specifier) {
    return requestingModule.require(specifier)
}

And call it the following way:

return { module: requireModule(module, modulePath), modulePath: modulePath, error: undefined };

The resulting code will behave 1:1 in Node, while not triggering any warnings in bundlers trying to resolve these dynamic calls.

@RyanCavanaugh RyanCavanaugh added Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript labels Jul 14, 2020
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jul 14, 2020
@antoineol
Copy link

Hi,

Same thing here, when typescript is bundled with create-react-app (typescript is behind ts-morph), I have 3 warnings:

./node_modules/typescript/lib/typescript.js
Critical dependency: the request of a dependency is an expression

./node_modules/typescript/lib/typescript.js
Critical dependency: the request of a dependency is an expression

./node_modules/typescript/lib/typescript.js
Module not found: Can't resolve 'perf_hooks' in 'C:\Users\User\perso\app\nocode-front\node_modules\typescript\lib'

It seems to work on a sample code, although I can't say whether all features are here.

It can be reproduced by creating a react app (e.g. npx create-react-app my-app --template typescript) then adding the following in any of the files (e.g. index.tsx or App.tsx):

import typescript from "typescript";

console.log(typeof typescript !== 'undefined'); // To force including typescript in the bundle

Side note: when I try on codesandbox, no warning. On stackblitz, it complains that "perf_hooks" module is not found and it's blocking the bundling (installing "perf_hooks" in the dependencies doesn't solve it).

@elevatebart
Copy link

@RyanCavanaugh I am also interested in this.
Do you think I should create a Pull Request with the suggested fix?

@jtbandes
Copy link
Contributor

jtbandes commented Apr 9, 2021

I ran into a similar issue, and worked around it by just removing the dynamic require()s from node_modules/typescript/lib/typescript.js at build time using string-replace-loader: https://github.com/foxglove/studio/pull/546

@AviVahl
Copy link
Author

AviVahl commented Apr 9, 2021

A cleaner workaround which we've been using for a while now (several typescript versions) is to tell webpack to avoid parsing typescript.js:

/** @type {import('webpack').Configuration} */
module.exports = {
    // ...
    module: {
        rules: [
            // ...
        ],
        noParse: [require.resolve('typescript/lib/typescript.js')],
    },
    // ...
};

Works with both webpack@4 and webpack@5.
The above configuration (which uses require.resolve) assumes there's a single copy of typescript being bundled, and it's the same one available to the webpack.config.js file itself.

@WilliamTepet
Copy link

Angular V. 9

I had the same issue, however it was caused for this code import {toTitleCase} from "codelyzer/util/utils";

For some reason that caused the error, so I just removed it from the file where I was doing the import and the project compiled without any errors/warnings.

Just in case anybody else had the same problem.

@replaysMike
Copy link

I've tried @AviVahl 's recommended solution but it doesn't seem to do anything. In my case I'm using react-app-rewired with the following:

config-overrides.js

const webpack = require('webpack');
module.exports = function override(config, env) {
  // ....
  config.plugins.push(
    new webpack.ProvidePlugin({
      process: 'process/browser',
      Buffer: ['buffer', 'Buffer'],
    }),
  );
  config.module.noParse = [require.resolve('typescript/lib/typescript.js')];
  return config;
}

I still get the following error on build and haven't found a working solution yet, maybe I'm doing something wrong.

Module not found: Error: Can't resolve 'perf_hooks' in 'node_modules\typescript\lib'

Critical dependency: the request of a dependency is an expression

@AviVahl
Copy link
Author

AviVahl commented Mar 28, 2022

@replaysMike according to https://github.com/timarney/react-app-rewired#1-webpack-configuration---development--production

It is also not able to be used to customise the Webpack Dev Server that is used to serve pages in development mode because create-react-app generates a separate Webpack configuration for use with the dev server using different functions and defaults.

EDIT: oh, you wrote "on build", so it might be something else.
Needless to say, we're still using the workaround on my team and everything works. Though our webpack config is not abstracted away by some third-party...

@replaysMike
Copy link

@AviVahl ty. That's why I was using react-app-rewired in order to customize webpack 5 via config-overrides.js. I don't know if this is recommended, but I was able to solve it just now with:

// config-overrides.js
module.exports = function override(config, env) {
  // ...
  config.module.noParse = /typescript/;
  config.ignoreWarnings = [/Failed to parse source map/];
}

It seems webpack noParse ignored the path as a string and required an expression - perhaps this is a new change with webpack 5?

@AviVahl
Copy link
Author

AviVahl commented Mar 29, 2022

We're using it with webpack5. Perhaps you're on windows and experiencing C: vs c:? If launching from vscode, it messes up the drive letter (forces it to be lowercased)

@replaysMike
Copy link

hmm that's a possibility. require.resolve() was resolving it to: B:\\gitrepo\\personalcode\\Binner\\Binner\\Binner.Web\\ClientApp\\node_modules\\typescript\\lib\\typescript.js

and the error was showing "Can't resolve 'perf_hooks' in":
B:\gitrepo\personalcode\Binner\Binner\Binner.Web\ClientApp\node_modules\typescript\lib

Perhaps internally it's not doing the comparison using double slashes?

@AviVahl
Copy link
Author

AviVahl commented Mar 30, 2022

require.resolve returns absolute paths, so on Windows it uses just a single slash as separator. If anything stringifies that absolute path, the separators will show up as escaped slash (double slash). Perhaps react-rewired is doing something to that string when merging configs?

Another possibility is that you've inspected two different contexts: one while debugging and seeing the slash as escaped, and another seeing a printed error by webpack, where the slash will show as single.

To better understand what's going wrong, I'd pass a function to noParse and compare the paths that come in to the result of require.resolve: https://webpack.js.org/configuration/module/#modulenoparse

Add the following somewhere in the webpack config:

console.log(require.resolve("typescript/lib/typescript.js"));

and:

noParse: content => {console.log(content); return false;}

Because it's Windows, I'd bet it has to do with case insensitivity of paths.

@disambiguator
Copy link

To add to this, I tried the solution described in #39436 (comment), and while it did make the warnings go away, it also completely squashed all output coming from webpack.

I.e. I no longer saw messages like

Version: webpack 4.46.0
Time: 91795ms
Built at: 07/28/2022 2:54:31 PM
                                                   Asset      Size                                             Chunks                                Chunk Names
                                                    0.js   702 KiB                                                  0  [emitted]              [big]  
                                                    1.js   321 KiB                                                  1  [emitted]              [big]  
                                                   10.js  6.09 KiB                                                 10  [emitted] 

and instead just

webpack built 87d9467409f36894a95e in 91766ms

Hopefully this gets fixed internally to TypeScript.

@michalczerwinski
Copy link

I have the same issue in recent create-react-app, unfortunately any of the above doesn't work. I use react-app-rewired, webpack 5 and config-overrides.js

Any chance we can introduce the suggestion from @AviVahl ?

@wrightwriter
Copy link

wrightwriter commented Jun 4, 2023

@replaysMike 's solution works for me in current webpack/ts/etc versions!

@yoshikiohshima
Copy link

I am trying to compile typescript code in the browser and stumble upon this problem. It works in a simple isolated test case I wrote works, but when I move it to a larger production code it fails because of this "can't resolve 'perf_hooks'" error, and adding the noParse rule does not solve it (it rather moves the error from the bundle time to the run time). What is the solution? My current solution is to load the package from A CDN and that works... But I'd rather resolve it at the bundlign time. https://github.com/croquet/microverse/blob/ac3a5412ad6b4ec860e209bec9836fd9f33cf2e8/prelude.js#L63

@jakebailey
Copy link
Member

Didn't realize this issue existed; one thing you may try is to just use an IgnorePlugin to bulk ignore all require calls; if you're bundling for the browser they won't be used anyway.

I have not tested, but something like:

new webpack.IgnorePlugin({
  resourceRegExp: /.*/,
  contextRegExp: /typescript.js$/,
});

Though if you have an easy repro I'd be happy to play around with it.

@yoshikiohshima
Copy link

Thanks for a reply. This IgnorePlugin does not work for me (I tried some different RegExps). Yes, I'll try to make a smaller repro... but that has been hard. I can make a branch on the above microverse repo that changes the script line with import to test it, if you are willing to try it in that form.

@yoshikiohshima
Copy link

Didn't realize this issue existed; one thing you may try is to just use an IgnorePlugin to bulk ignore all require calls; if you're bundling for the browser they won't be used anyway.

I have not tested, but something like:

new webpack.IgnorePlugin({
  resourceRegExp: /.*/,
  contextRegExp: /typescript.js$/,
});

Though if you have an easy repro I'd be happy to play around with it.

This branch, which is not an isolated test case though, shows the issue: https://github.com/croquet/microverse/tree/ts-import-test

@jakebailey
Copy link
Member

jakebailey commented Oct 10, 2023

@yoshikiohshima I finally had time to look at the above; the issue here comes down to the fact that our package.json doesn't include perf_hooks in the browser section of package.json, which was unintentional. I've sent #56062 for that.

@yoshikiohshima
Copy link

thanks!

@curran
Copy link

curran commented Nov 12, 2023

FWIW there's a similar warning that occurs when bundling for the browser using Vite. It looks like this:

> vite build

vite v4.5.0 building for production...
[plugin:vite:resolve] Module "perf_hooks" has been externalized for browser compatibility, imported by
"/home/curran/repos/vzcode/node_modules/typescript/lib/typescript.js". See 
http://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility 
for more details.

Fingers crossed that the changes in #56062 solve this issue as well! Thank you for the fix.

@jakebailey
Copy link
Member

It definitely should; you can try out the RC or nightly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests