react lazy load external amd modules - reactjs

My react app has external resources outside src/ so i have ejected react-scripts and disabled ModuleScopePlugin.
Referenced the external library in resolve.alias and used across the application.
resolve.alias: {
'genlib': path.resolve(fs.realpathSync(process.cwd()), 'lib/genlib/js/src'),
'config': path.resolve(fs.realpathSync(process.cwd()), 'config/dev'),
'messages': path.resolve(fs.realpathSync(process.cwd()), 'config/messages')
}
genlib is the external library im trying to reference.
The external library is AMD using requirejs.
One of the file in the library lazy loads a class using require.
define('class1', ['require', ...], function(require, ...) {
//
require([variable], function()...)
});
The above require is throwing Cannot find module 'xxx' at runtime from webpackEmptyContext.
When require from above code is consoled then below is logged instead of require function. Confused why webpackEmptyContext is consoled out instead of webpackContext function
ƒ webpackEmptyContext(req) {
var e = new Error("Cannot find module '" + req + "'");
e.code = 'MODULE_NOT_FOUND';
throw e;
}
I have not changed any of the webpack.config.js except adding alias and disabling ModuleScopePlugin.
What else needs to be added or changed in config to lazy load amd modules.
webpack v4.19.1
react-dev-utils v7.0.1

I have solved by using ContextReplacementPlugin.
Added below code to webpack config plugins.
new webpack.ContextReplacementPlugin(/genlib[\\/]services/, /.*$/),
Now a map is created with all the files in the services directory and webpackContext loads the files when required.

You will see babel-loader in return object of webpack.config.js file. module -> rules array First code is to run the linter
{
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
],
include: [
paths.appSrc,
'paht/to/external-library/using/requirejs' <---- Add your external file path for loader to parse the AMD files
],
}
Similarly include file path to test entry of JS files
test: /\.(js|mjs|jsx|ts|tsx)$/,
Can you try this and check?

Related

How can I fix the "Failed to parse source map from ..." errors in my ReactJS/TS project

I have a ReactJS project where I wanted to use a Barcode-Scanner npm module with the name html5-qrcode, but I always get this error:
Failed to parse source map from 'C:\...\node_modules\html5-qrcode\third_party\index.js.map' file: Error: ENOENT: no such file or directory, open 'C:\...\node_modules\html5-qrcode\third_party\index.js.map'
And then there are errors like: (seperated for readability)
WARNING in ./node_modules/html5-qrcode/esm/camera/core-impl.js Module Warning (from ./node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map from 'C:\...\node_modules\src\camera\core-impl.ts' file: Error: ENOENT: no such file or directory, open 'C:\...\node_modules\src\camera\core-impl.ts'
I thought it might be an TS error, because every file of the second error part has a .ts ending.
So I made a new ReactTS project with all components and co in it, but I still get the same error.
I thought it might be an TS error, because every file of the second error part has a .ts ending. So I made a new ReactTS project with all components and co in it, but I still get the same error.
It seems like the npm package has issues with its source maps and webpacks's source-map-loader module is not able to process them. This doesn't really affect the application itself but having all those warnings is annoying.
I came across two solutions: either force the source-map-loader to skip the culprit package or ignore source-map warnings all together.
To achieve either solution, you'll need to be able to override the webpack.config.js. How to override it really depends on the framework you use to run your React apps (I have mine setup using NX)
Solution 1: Ignore source-mapping warnings (Easiest)
Add ignoreWarnings: [/Failed to parse source map/] to your webpack configuration.
E.g.
const { merge } = require('webpack-merge');
module.exports = (config) => {
return merge(config, {
ignoreWarnings: [/Failed to parse source map/]
});
};
Your webpack.config.js will look a lot different than this.
The idea is to add (or override) ignoreWarnings with the pattern of the message it should ignore.
Solution 2: Skip source-map-loading for culprit package (Cleanest?)
const { merge } = require('webpack-merge');
module.exports = (config, context) => {
return merge(config, {
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
use: [
{
loader: 'source-map-loader',
options: {
filterSourceMappingUrl: (url, resourcePath) => {
// #zxing has issues with its source maps
if (/#zxing/i.test(resourcePath)) {
return false;
}
return true;
}
}
}
]
}
]
}
});
};
The idea here is to override the source-map-loader rules and skip its execution if the current resource matches the regex. In my case, I want to skip any resource that contains #zxing.
I tried using hte exclude option but I had no luck with that and opted to use the filterSourceMappingUrl instead. Maybe it works for you though. Remember, the pathing has to be absolute so you might need to adapt the excluded pathings.
More details here
const { merge } = require('webpack-merge');
module.exports = (config, context) => {
return merge(config, {
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
use: ['source-map-loader'],
exclude: ['/node_modules/#zxing']
}
]
}
});
};
Hope this helps.
Cheers.
add react-app-rewired to your project:
yarn add --dev react-app-rewired
or
npm install react-app-rewired --save-dev
modify your package.json with those lines:
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
create a file called config-overrides.js in project root (same folder as package.json) with this content:
module.exports = function override(config) {
return {
...config,
ignoreWarnings: [
{
module: /node_modules\/stylis-plugin-rtl/,
},
],
}
}
change "stylis-plugin-rtl" in config-overrides.js above to whichever name of library that has an invalid build and throwing errors.
source maps errors should no longer appear.

No event handlers fire with React production build using Rollup

In our project we're using Rollup to build/bundle our code with code-splitting via React lazy on our routes. With a development build, everything works fine; our button and dropdown event handlers fire. In a production build, none of the handlers fire. It gets more interesting. If we don't do code-splitting at the route level and just have a single bundle file with everything, both development and production builds work perfectly. I'm wondering if I'm missing an option for any of my Rollup plugins that is causing this behavior?
envVars.exclude = 'node_modules/**';
envVars.preventAssignment = true;
const minifier = isDebugBuild ? null : terser();
rollup({
preserveEntrySignatures: false,
input: paths.scripts.src,
plugins: [
replace(envVars), // Replace all env vars.
nodeResolve({ // Resolve any imported node modules.
browser: true // Ensure all node modules are ready for browser use.
}),
json(), // Convert imported json files as modules.
commonjs({
include: 'node_modules/**'
}),
babel({ // Have Babel transpile our scripts to es6.
babelHelpers: 'runtime', // Include Babel runtime helpers in our scripts.
exclude: 'node_modules/**' // Don't transpile any modules imported from node.,
}),
minifier
]
})
.then((bundle) => {
const entryFileOutputName = isDebugBuild ? paths.scripts.entryFile : paths.scripts.entryFileMinifiedName;
const chunkFileOutputName = isDebugBuild ? paths.scripts.chunkFile : paths.scripts.chunkFileMinifiedName;
return bundle.write({
dir: paths.scripts.build, // The final output directory.
entryFileNames: entryFileOutputName, // The main entry script.
chunkFileNames: chunkFileOutputName, // The chunk scripts that will be lazy loaded.
format: 'es', // The final format of the bundles scripts. Here we want ES6+ JS.
sourcemap: isDebugBuild // Generate source maps or not.
});
});

Webpack pdf file loader

This is the error:
"Build failed!
× ERROR ./media/fonts/handFont3.otf 1:4.
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
# ./index.js 3:0-37
# ../node_modules/preact-cli/lib/lib/entry.js
# multi ../node_modules/preact-cli/lib/lib/entry webpack-dev-server/client webpack/hot/dev-server"
I'm trying to import pdf into preact component to make it build so I can get the link to the page with pdf file. Here is how:
import pdfFile from '../../media/images/pdfFile.pdf'
<a href={pdfFile} target="_blank"...
It didn't work, so I Googled this two solutions to add in wepback.config.js:
module.exports = {
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|pdf)$/,
use: ['file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
},
]
},
};
and
{
test: /\.(pdf)$/,
use: ['url-loader']
},
I do manually make webpack.config work by this string:
--config webpack.config.js
And file is working, but result is the same. I'm still getting the error. And the same with otf fonts.
Thank you.
I figured out the solution. You can't use webpack config with preact config together.
So I removed webpack config and changed preact one to this:
export default (config, env, helpers, options) => {
const rule = {
test: /\.(otf|pdf)$/,
loader: 'file-loader'
}
config.module.rules.push(rule);
}

Adding uncompiled typescript library to Craco config

I have a "common library" written in Typescript and I want to use it in a couple more projects. I don't want to create a npm package from it, I just want to import it as "uncompiled package" but I'm having trouble setting this up.
I tried adding ts-loader and excluding the module like this:
webpackConfig.module.rules.push({
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules\/((?!common-library).)*$/,
});
However, this results in Error: TypeScript emitted no output for /path/to/index,tsx.
I also tried using craco-babel-loader https://github.com/rjerue/craco-babel-loader, however when adding it to the list of plugins:
{
plugin: rewireBabelLoader,
options: {
includes: [resolveApp('node_modules/common-library')],
excludes: [/node_modules/],
}
},
I'm getting the following:
C:\path\to\app\node_modules\#craco\craco\lib\loaders.js:34
rules.some((rule, index) => {
^
TypeError: rules.some is not a function

How to use ng-annotate with hybrid app based on angular-cli

I'm working on an Angular.js project written with TypeScript. We're trying to evaluate whether to upgrade to Angular 8 and we're stuck with how to use ng-annotate with angular-cli's webpack configuration.
I believe that this can be achieved either by using the #angular-builders/custom-webpack tool or by using ngx-build-plus tool but I had not succeeded with neither of them.
My current attempt includes a partial webpack file for ngx-build-plus with the following configuration :
module: {
rules: [
{
test: /\.ts$/,
loaders: ['ng-annotate-loader?ngAnnotate=ng-annotate-patched'],
},
{
test: /\.tpl\.html$/,
loader: 'ng-cache-loader?-url&module=templates&prefix=src:./**/'
}
]
},
};
Having this, when I run ng serve --extra-webpack-config webpack.partial.js -o I get the following error : NonErrorEmittedError: (Emitted value instead of an instance of Error) error: couldn't process source due to parse error,Unexpected token
The token to which it refers, is simply the type declaration for a method parameter. So I'm guessing that there is some conflict with the loader that angular-cli already uses for TypeScript files but I don't know how to resolve this.
Is there any input on how to solve this either using one of the two tools or something else?
So, the way to do this is by using webpack-merge and custom-webpack.
This is the configuration to run ng-annotate with Typescript files :
module.exports = (config, options) => {
const customConfig = {
module: {
rules: [
{
test: /\.ts$/,
loaders: ['ng-annotate-loader?ngAnnotate=ng-annotate-patched'],
},
{
test: /\.tpl\.html$/,
loader: 'ng-cache-loader?-url&module=templates&prefix=src:./**/'
}
]
}
};
return merge.strategy({
'module.rules': 'prepend'
})(config, customConfig)
};
The key part is the merge.strategy call which will make sure that the loaders of the custom configuration will be prepended to the ones that angular-cli already sets up.
After several hours of looking at alternatives, the solution that worked the best for me was using babel-plugin-angularjs-annotate by:
Creating a new project
Installing babel and babel-plugin-angularjs-annotate
Executing babel, which takes the files from src, adds the annotations, and puts the result in the folder output.
For others needing the complete solution, this is what I executed:
mkdir babel-project && cd babel-project
npm init -y
npm install -D babel-cli babel-plugin-angularjs-annotate
Create .babelrc and add this:
{
"presets": [],
"plugins": [ "angularjs-annotate" ]
}
Finally execute babel:
npx babel src -d build --extensions .ts
This takes files like:
import * as _ from "underscore";
import "#app2/hello/chao"
const greeting = () => "Hello World"
angular.module("MyMod")
.controller("MyCtrl", ($scope, HelloClient) => {
HelloClient.add($scope, greeting);
});
And turns them into:
import * as _ from "underscore";
import "#app2/hello/chao";
const greeting = () => "Hello World";
angular.module("MyMod").controller("MyCtrl", ["$scope", "HelloClient", ($scope, HelloClient) => {
HelloClient.add($scope, greeting);
}]);
Update
Using babel-plugin-angularjs-annotate messed a lot with my formatting, so I ended up copying&pasting just the relevant parts of the Babel output, which took some hours.

Resources