Import multiple files in folder in React / Nextjs - reactjs

I'm trying to import multiple files with a certain extension in a folder:
const allEntries = require.context('../static/blog', true, '/\.md/')
but I'm getting:
Unhandled Rejection (TypeError): __webpack_require__(...).context is not a function
I'm using Nextjs and require the files in one of the pages. Something seems off here?
Edit: I don't necessarily need to do it via require I just want to be able to import/require multiple files at once without knowing the filename or how many of the files are in a folder.

You can give the following a try in webpack:
const glob = require('glob');
const allEntries = glob.sync("../static/blog/*.md");
The glob will return an array of files. The array will contain all files with .md extension in the ../static/blog/ folder. Eventough there is a package it shouldn't be required to install the package.

Try using require context npm library.
$ npm i --save require-context
In your file:
// Load globally into all modules.
require('require-context/register')
// Load locally as a function.
var requireContext = require('require-context');
function requireAll(r) { r.keys().forEach(r); }
requireAll(requireContext('../static/blog', true, /\.md$/));

From what I understand you are very close, from the error you are using webpack's require.context
const allEntries = require.context('../static/blog', true, '/\.md/')
console.log(allEntries.keys()) // all the files found in the context
allEntries.keys().forEach(allEntries) // require them all

const imageDirectory = path.join(process.cwd(), '/public/dirname');
const imageFilenames = await fs.readdir(imageDirectory)
// Store the file names in an array and use it.
webpack.require is basically using webpack, make sure what you're using is webpack in fact

Related

React component library can not load its own static asset

I have a react app (using create react app) and then a component library (using webpack directly). One component in the component library needs to load a png file in one of the components that it exports. In the webpack config for the component library I have a section such as:
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
This successfully results in a file like: 29b6b66cf1a691be2b3f.png in the component libraries output directory. The issue, is that when the application uses that component and the component attempts to load the image, the img element is <img ... src="29b6b66cf1a691be2b3f.png" /> and that fails to load, since that image actually lives in the component library folder (within the react application's node_modules/component-library/ at that point).
I have scoured the internet to the best of my ability, and can not seem to figure out what the best solution would be here. Any help is appreciated. I will quickly offer clarification if needed.
UPDATE: I have discovered CopyWebpackPlugin but it is quite unfortunate that this would require me to eject the "parent" application from create react app. Something I would very much prefer to avoid.
UPDATE2: Current plan is to try following something like what is explained here. The jist of it is to utilize something like rewire to avoid needing to eject and still be able to edit the webpack config via something like:
// in ./build.js
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
// edit webpack config values here
... I will answer my own question here if I find that this approach works.
Alright, well this felt like it was much messier than it needed to be, but I was able to figure out a way to accomplish this. The pieces were as follows.
First, needed to install copy-webpack-plugin. This was not as simple as it might sound, because I needed to find one that would not conflict with the version of webpack required by my react-scripts (create react app). I determined that copy-webpack-plugin#6.4.1 was the last version to support webpack v4, so I installed that and then added the following to my package.json:
"overrides": {
"copy-webpack-plugin": {
"webpack": "4.44.2"
}
},
this ensured that it would use the version of webpack that my react-scripts installation was expecting.
Then, I also needed to install rewire, and add the following to a file called build.js:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
const CopyPlugin = require("copy-webpack-plugin");
const patterns = [
{
context: "node_modules/component-library/path/",
from: "*.png",
to: "."
},
]
if(config.plugins === undefined){
config.plugins = [new CopyPlugin({patterns})]
}else{
config.plugins.push(new CopyPlugin({patterns}))
}
// below lets it work in dev mode too
if(config.devServer === undefined){
config.devServer = {
writeToDisk: true
}
}else{
config.devServer.writeToDisk = true;
}
Finally, had to update my build script to be:
"scripts": {
...
"build": "node ./build.js",
...
},

React : webpack.config.js modification to import json variables in a sass file?

I am trying to import variables from .json file in a .scss file with node-sass-json-importer package.
I am facing a problem because this package is not automatically integrated in react-scripts/config/webpack.config.js. So, I would like to modify this file as follows below :
Add const jsonImporter = require ('node-sass-json-importer');
Add an optional preProcessorOptions object parameter to getStyleLoaders function. Indeed, this function has no preprocessor options const getStyleLoaders = (cssOptions, preProcessor) and the only option added by default is sourceMap: true. Of course, this function will take in account this new parameter.
Add a third parameter in the getStyleLoaders call for scss file.
{
implementation: require("sass"),
sassOptions: {
importer: jsonImporter(),
}
}
It works on a minimal webpack implementation (without react). But, I suppose it is not so easy to apply changes to react-scripts/config/webpack.config.js and I suspect I will have many problems. Perhaps, there is an another way to do it.
Thanks for answer.

Bundle CRA into single js file

I'm having a React App created with CRA, I need to deploy it as a widget.
People need to be able to include a js file and then just use the <App></App> Tag where ever they need it. Since CRA splits files I already tried ejecting the app, but failed miserably.
I already tried from similar questions the following:
combine-react-build-output-into-single-js-file
bundling without minification
the bundling without minification almost got my to the end. But now I would need to minify the resulting file.
This is my build.js file:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
// Consolidate chunk files instead
config.optimization.splitChunks = {
cacheGroups: {
default: false
}
};
// Move runtime into bundle instead of separate file
config.optimization.runtimeChunk = false;
// JS
config.output.filename = '[name].js';
// CSS. "5" is MiniCssPlugin
config.plugins[5].options.filename = '[name].css';
config.plugins[5].options.publicPath = '../';
this file comes from the bundling without minification question. I'm now fairly clueless how to minify it and if it is really the right way to deploy.

AudioWorklet error: DOMException: The user aborted a request

I've successfully instantiated a simple AudioWorklet in React and wish to start a simple oscillator like in Google's example. In order to test run it, I am rendering a button whose onClick event calls the following:
src/App.jsx:
userGesture(){
//create a new AudioContext
this.context = new AudioContext();
//Add our Processor module to the AudioWorklet
this.context.audioWorklet.addModule('worklet/processor.js').then(() => {
//Create an oscillator and run it through the processor
let oscillator = new OscillatorNode(this.context);
let bypasser = new MyWorkletNode(this.context, 'my-worklet-processor');
//Connect to the context's destination and start
oscillator.connect(bypasser).connect(this.context.destination);
oscillator.start();
})
.catch((e => console.log(e)))
}
The problem is, on every click, addModule method is returning the following error:
DOMException: The user aborted a request.
I am running Chrome v66 on Ubuntu v16.0.4.
src/worklet/worklet-node.js:
export default class MyWorkletNode extends window.AudioWorkletNode {
constructor(context) {
super(context, 'my-worklet-processor');
}
}
src/worklet/processor.js
class MyWorkletProcessor extends AudioWorkletProcessor {
constructor() {
super();
}
process(inputs, outputs) {
let input = inputs[0];
let output = outputs[0];
for (let channel = 0; channel < output.length; ++channel) {
output[channel].set(input[channel]);
}
return true;
}
}
registerProcessor('my-worklet-processor', MyWorkletProcessor);
My code is straight JavaScript, not React, but I got the same error because the path provided to addModule was incorrect. In my case, both the script that calls addModule and the script provided as the argument to addModule reside in the same directory ("js"). In spite of that, I still had to include this directory in the path to eliminate the error:
...addModule('js/StreamTransmitter.js')...
I hope this helps. Good luck!
For anyone else getting this mysterious error, swallow your pride and check the following:
The processor doesn't have any errors.
The processor is calling external modules with proper path to the external file(s).
The external modules don't have any errors.
The promise will abort when external modules that are loaded via "import" have errors, or the paths to the modules can't be resolved (e.g. the path's to the modules are wrong and don't point to existing files).
This worked for me: serve your worklet files from public folder instead of src. The addModule(url) function points there by default, so addModule('worklets/foo.js') references file public\worklets\foo.js
Source: https://hackernoon.com/implementing-audioworklets-with-react-8a80a470474
This seems to be a bug in the Chromium module loader, it parses the worklet/processor.js file by removing whitespace, which in turn causes it to have JavaScript syntax errors everywhere, which then finally causes this generic non-explanatory error message to show up.
The solution is to serve your worklet-processors (e.g. worklet/processor.js in your case) with:
Content-Type: application/javascript
or
Content-Type: text/javascript
I also experienced this error but due to a Webpack issue.
Turns out webpack doesn't support worklets like it supports web workers (see this issue).
I would recommend using worker-url with webpack.
Install worker-url
npm i --save-dev worker-url
Update your webpack config to include the WorkerUrl plugin.
const WorkerUrlPlugin = require('worker-url/plugin');
module.exports = {
// ...
plugins: [new WorkerUrlPlugin()],
// ...
};
Use WorkerUrl like so:
import { WorkerUrl } from 'worker-url';
const workletUrl = new WorkerUrl(
new URL('./random-noise-processor', import.meta.url),
{ name: 'worklet' },
);
await context.audioWorklet.addModule(workletUrl);
The Error "DOMException: The user aborted a request." happens when the AudioWorklet.addModule() function cannot load the file from the path or URL you provided. Refer to this MDN page
The api AudioWorklet.addModule() expects a String containing the URL of a JavaScript file with the module to add.
It can be an internal URL that points to your public folder where the browser loads your static files in this case -> 'worklet/processor.js if the worklet folder is inside the public directory of your React app.
You can modify your code as below.
this.context.audioWorklet.addModule('worklet/processor.js')
In this case the audioWorklet.addModule() method expects the path to point to your public folder. It can also be an external URL for example a link to Github repository that loads the JS file.
Changing:
this.context.audioWorklet.addModule('worklet/processor.js')
with
this.context.audioWorklet.addModule('../worklet/processor.js')
worked for me.

`requestAnimationFrame` polyfill error in Jest tests

Got this error after upgrading to React when I ran my Jest unit tests:
React depends on requestAnimationFrame. Make sure that you load a polyfill in older browsers.
How do I fix it?
I'm using Jest 18.1.0.
Found a workaround!
Steps:
Create the file __mocks__/react.js
Add the following into __mocks__/react.js
const react = require('react');
// Resolution for requestAnimationFrame not supported in jest error :
// https://github.com/facebook/react/issues/9102#issuecomment-283873039
global.window = global;
window.addEventListener = () => {};
window.requestAnimationFrame = () => {
throw new Error('requestAnimationFrame is not supported in Node');
};
module.exports = react;
Run jest !
As marked on comments on the code
This is the solution from
https://github.com/facebook/react/issues/9102#issuecomment-283873039
this worked for me:
Install raf
npm install --saveDev raf or yarn add -D raf
Add the polyfill to your setupFiles in your jest config in package.json like this:
'setupFiles': ['raf/polyfill']
Note: if you have other setup files in this array, you may want to put raf/polyfill first.
If you just need to polyfill it for tests, then you don't actually need the throttling.
Create a new file with this code:
global.requestAnimationFrame = function (cb) {
return setTimeout(cb, 0);
};
Add that file to the jest/setupFiles array in your package.json.
If you are using create-react-app, some of these solutions will not work well (or at all, in the case of setupFiles). What does work well is creating a file at src/setupTests.js and adding your mock in there:
global.requestAnimationFrame = (cb) => { cb(); };
You can also add other global mocks in there (e.g. localStorage and navigator.serviceWorker).
Another working solution!
The idea is to load a simple shim before each spec, by using the setupFiles property in the jest config.
Create a file shim.js file (preferably in your root dir) and have this code in it:
global.requestAnimationFrame = (callback) => {
setTimeout(callback, 0);
};
Next, you may be having redundant code that keep reappearing in all/most of your files - and you want to put them in a single file and have them run before each spec also, to do that:
Create a setup.js file in the root dir too. A good piece of redundant code to D.R.Y is the react enzyme adapter configuration code. Paste it here
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });
Now create the jest.config.js file, to specify the paths of the two files
{
module.exports = {
"setupFiles": ["<rootDir>shim.js", "<rootDir>setup.js"]
}
}
N.B: The jest config file take json, so make sure json's in. Also, if your shim.js and setup.js files are not in the same dir as your jest.config.js, adjust the path accordingly.
Hope this helps!
Credit: https://github.com/facebook/jest/issues/4545
Here's a realistic way to mock requestAnimationFrame:
let time;
const maxTimeElapsed = 2000;
beforeEach(() => {
time = 0;
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => {
time += 100;
if (time < maxTimeElapsed) {
return cb(time) as any;
}
});
});
in your test, it repeatedly calls the RAF callback until it reaches the max elapsed time that you set. It happens instantly, so you don't need to stall it.
Just upgrade your react-scripts to 1.0.15 or above. It has been officially fixed after that version. See more details in https://github.com/facebook/create-react-app/issues/3199
Search the comment of gaearon commented on 31 Oct 2017
If you use TypeScript, the best solution is;
window.requestAnimationFrame = (): number => {
window.clearTimeout();
return 0;
};
Before all your describe and test suits.
According raf package docs at this moment need to run polyfill() function and specify object that need to be polyfilled. In my case works:
require('raf').polyfill(global.window)
Turns out it was because I upgraded enzyme without upgrading react and react-dom.
React 15.5 brings about some deprecations that caused many dependent libraries to have to update too. Make sure you are updating react, react-dom and check the README of those dependent packages for new libraries you have to install. For instance, Enzyme 2.8.2 now requires react-addons-test-utils as a dependency.

Resources