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?!
It executes React code and understands JSX and modern JavaScript features. It does that without using Babel.
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.
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.
Require.context
it is a webpack feature, not cra
or something else.
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".
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With