javascript - ¿Cómo hacer que require.context funcione con Create React App con/sin Craco?
Estoy intentando hacer algunos requires durante la ejecución a través de require.context en mi proyecto CRA (con Typescript), pero solo estoy obteniendo este tipo de errores:
TypeError: __webpack_require__(...).context no es una función
y
Dependencia crítica: la función require se usa de una manera en la que las dependencias no pueden ser extraídas de forma estática
Leí en algún lugar que esto ahora necesita ser polyfilled a través de Babel o cra-rewired. Bueno, ya estoy usando Craco para habilitar el soporte de Less, pero no tengo idea de cómo agregar require.context a mis configuraciones de Craco.
¿Alguien sabe cómo hacer esto?
Actualización: Así es como estoy llamando a require.context:
const packagesDirectory = path.join(__dirname, '../../../../packages');
const textDataContext = require.context(packagesDirectory, true, /(\w+)\.(\w+)\.(mdx?)/);
Actualización 2:
Como sugieren algunos de los comentarios en este hilo, he intentado agregar babel-plugin-require-context-hook a mi aplicación de la siguiente manera:
// craco.config.js
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{plugin: CracoLessPlugin}
],
babel: {
plugins: ['require-context-hook']
}
};
Y luego he intentado llamar a require.context así:
// 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?)/);
Pero luego recibo este error:
TypeError: fs.readdirSync no es una función
😕
Actualización 3:
Parece que CRA sí soporta require.context sin necesidad de ningún polyfill. Pero parece que falla cuando se ejecuta a través de un módulo importado. En mis intentos anteriores, he estado ejecutando llamadas a require.context en myfile.js (ver arriba) que ha sido importado por index.js de la siguiente manera:
// index.js
import myModules from 'myfile.js';
ReactDOM.render(...);
Sin embargo, si cambio index.js a esto:
// index.js
const something = require.context('../../packages/', true, /(\w+)\.(\w+)\.(mdx?)/);
something.keys().forEach(key => console.log(key));
ReactDOM.render(...);
¡Funciona de maravilla! ¿¡Por qué?!
Respuesta #1
Require.context es una característica de webpack, no de cra ni de otra cosa.
Entonces, ¿por qué no funciona con tu upd1 o upd2 y sí con upd3?
La respuesta es bastante simple: debido a que se usa una variable aquí. Webpack necesita que tu código sea estáticamente analizable. Esto significa que cuando escribes, por ejemplo, require.context('../src/directory/', true, /.ts$/), webpack piensa, hmm, OK, necesito encontrar y preparar todos los archivos .ts en src/directory de forma recursiva porque puede ser usado en pasos posteriores.
Y no puede funcionar con variables porque require.context(myPathVariable, true, /.ts$/) puede ser cualquier cosa. Webpack no sabe qué es myPathVariable en la fase de build porque se calculará solo en la fase de runtime.
Esta regla también trata sobre importaciones dinámicas. import('../src/keks/index.ts') funcionará, import(myVar + '../src/keks/index.ts') no funcionará.
Por favor, vea este problema con discusión y algunos consejos sobre la "estaticidad" de require.context.
Cómo hacer que funcionen las rutas dinámicas
Una forma de usar rutas dinámicas es utilizar DefinePlugin. Pero todas tus rutas dinámicas deben ser conocidas y calculadas en la fase de build (en la configuración de webpack o en cualquier script de node.js).
Ejemplo:
webpack.config.js
module.exports = {
plugins: [new DefinePlugin({ PACKAGES_DIR: JSON.stringify('path/to/packages') })]
}
y luego:
index.ts
puedes usar require.context(PACKAGES_DIR, true, /\.ts$/) o import(PACKAGES_DIR + 'myfile.ts') porque webpack ya sabe algo sobre estas rutas.
