Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?!

like image 647
o01 Avatar asked Jan 19 '20 11:01

o01


People also ask

Can you React work without Babel?

It executes React code and understands JSX and modern JavaScript features. It does that without using Babel.

Is it required to run NPX create React app my app every time when you need to create a React app?

If you are concerned with the size of it, you do not need to run create-react-app every time. You can make a react project yourself quite easily and by doing so you have much more control and understanding of your project.

Do I need Babel with create React app?

Creating a React application requires you to set up build tools such as Babel and Webpack. These build tools are required because React's JSX syntax is a language that the browser doesn't understand.


1 Answers

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.

like image 89
nickbullock Avatar answered Oct 29 '22 19:10

nickbullock