How to get require.context to work with Create React App with/withou Craco?
Asked Answered
B

1

7

I'm trying to do some requires during runtime through require.context in my CRA (with Typescript) project, but I'm only getting these kinds of errors:

TypeError: __webpack_require__(...).context is not a function

and

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

I read somewhere that this now needs to be polyfilled through Babel or cra-rewired. Well I'm already using Craco to enable Less-support, but I have no idea how to add require.context to my Craco configs.

Anyone know how to do this?

Update: This is how I'm calling require.context:

const packagesDirectory = path.join(__dirname, '../../../../packages');
const textDataContext = require.context(packagesDirectory, true, /(\w+)\.(\w+)\.(mdx?)/);

Update 2:

As some of the comments in this thread suggest, I've tried adding babel-plugin-require-context-hook to my app like so:

// craco.config.js

const CracoLessPlugin = require('craco-less');

module.exports = {
    plugins: [
        {plugin: CracoLessPlugin}
    ],
    babel: {
        plugins: ['require-context-hook']
    }
};

And then I've tried calling require.context like so:

// myfile.js

import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
registerRequireContextHook();

const packagesDirectory = path.join(__dirname, '../../../../packages');
const textDataContext = require.context(packagesDirectory, true, /(\w+)\.(\w+)\.(mdx?)/);

But then I get this error:

TypeError: fs.readdirSync is not a function

πŸ˜•

Update 3:

It seems CRA does support require.context without the need for any polyfills at all. But it looks like it fails when it is executed through an imported module. In my previous attempts I have been executing calls to require.context in myfile.js (see above) which has been imported by index.js like so:

// index.js

import myModules from 'myfile.js';

ReactDOM.render(...);

However, if I change index.js to this:

// index.js

const something = require.context('../../packages/', true, /(\w+)\.(\w+)\.(mdx?)/);
something.keys().forEach(key => console.log(key));

ReactDOM.render(...);

It works like a charm! Why?!

Busboy answered 19/1, 2020 at 11:50 Comment(3)
Did you write the require.context call correctly? Cause this type of error often due to incorrect function call. Provide some code. – Poplar
Updated question with code πŸ‘ – Busboy
try import '@babel/polyfill'; like here github.com/storybookjs/storybook/issues/… – Boeschen
T
4

Require.context it is a webpack feature, not cra or something else.

So why it doesn't work with your upd1 or upd2 and it does with upd3?

The answer is pretty simple - because variable is used here. Webpack needs your code to be statically analyzable. It means that when you write for example require.context('../src/directory/', true, /.ts$/) webpack thinks hmmm OK I need to find and prepare all .ts files in src/directory recursively because it can be used in further steps.

And it can't work with variables because require.context(myPathVariable, true, /.ts$/) can be anything. Webpack don't know what myPathVariable is at build phase because it will be calculated at runtime phase only.

This rule is also about dynamic imports. import('../src/keks/index.ts') will work, import(myVar + '../src/keks/index.ts') will not.

Please see this issue with discussion and some tips about require.context "staticness".

How to get dynamic paths working

One way to use dynamic paths is to use DefinePlugin. But all of your dynamic paths should be known and calculated at build phase (in webpack config or any node.js script).
Example:
webpack.config.js

module.exports = {
  plugins: [new DefinePlugin({ PACKAGES_DIR: JSON.stringify('path/to/packages') })]
}

and then:
index.ts
you can use require.context(PACKAGES_DIR, true, /\.ts$/) or import(PACKAGES_DIR + 'myfile.ts') because webpack already know something about this paths.

Teryn answered 22/1, 2020 at 7:31 Comment(1)
Thanks, looks like you're right! Bounty is all yours :) Frustrating how hard this was to figure out. I think the TypeError: __webpack_require__(...).context is not a function error was caused by a missing @types/webpack-env dependency however. Oh lord, deliver me from people who design incomprehensible error messages! – Busboy

© 2022 - 2024 β€” McMap. All rights reserved.