Using multiple Webpack loaders to transform SVGs into React components - reactjs

I am trying to combine several Webpack loaders together to load my SVG assets, transform them for inline usage and finally to convert the result into an actual React component that can be used in the application.
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: [
['env', {
browsers: supportedBrowsers,
modules: false
}]
]
}
}
},
{
test: /\.svg$/,
include: path.resolve(__dirname, 'src'),
use: [
{
loader: 'svg-inline-loader',
options: {
removeTags: true
}
},
{
loader: 'svg-react-loader'
}
]
}
]
}
}
If svg-react-loader is used on it's own the loader works as expected, however a lot of SVG assets have unneeded elements and attributes I would like to strip out using svg-inline-loader.
When the configuration is used as above the result will be loaded as text instead of executable JavaScript.
I am using the following loaders in my attempt to accomplish this:
https://github.com/webpack-contrib/svg-inline-loader
https://github.com/jhamlet/svg-react-loader

Would it be ok to use other loaders? You can combine svgo-loader with svg-inline-loader and use all the possible options provided by svgo.
If so just do npm i -D svgo-loader
And change your webpack config to this:
{
test: /\.svg$/,
use: [
'svg-react-loader',
{
loader: 'svgo-loader',
options: {
plugins: [
{cleanupAttrs: true}
]
}
}
],
exclude: /node_modules/
}

Related

importing katex.min.css into my React file and webpack.config.js just doesn't work

After an epic fight with my webpack, I need to go to bed. I honestly don't know wtf the issue is.
I tried now for hours and copied some webpack configs from the web together.
This is my webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('css-minimizer-webpack-plugin');
const nodeModulesPath = path.resolve(__dirname, 'node_modules');
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.json$/,
use: 'json-loader',
},
{
test: /\.(js)x?$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /\.(ts)x?$/,
exclude: /node_modules|\.d\.ts$/,
use: {
loader: 'ts-loader',
options: {
compilerOptions: {
noEmit: false,
},
},
},
},
{
test: /\.(scss|css)$/,
// exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'style-loader' },
{ loader: 'css-loader' },
// This is needed to help find the KaTeX fonts.
// https://github.com/bholloway/resolve-url-loader/issues/107
// https://github.com/bholloway/resolve-url-loader/issues/212
// https://stackoverflow.com/questions/54042335/webpack-and-fonts-with-relative-paths
// https://stackoverflow.com/questions/68366936/how-to-bundle-katex-css-from-node-modules-to-a-single-output-css-from-a-sass-us
'resolve-url-loader',
{
loader: "sass-loader",
options: {
// This is needed for resolve-url-loader to work!
// https://github.com/bholloway/resolve-url-loader/issues/212#issuecomment-1011630220
sourceMap: true,
sassOptions: {
includePaths: [nodeModulesPath],
},
},
},
],
},
],
},
plugins : [new MiniCssExtractPlugin()],
resolve : {
extensions: ['.css', '.tsx', '.ts', '.js'],
},
output : {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
}
};
I get the error message:
["..." | object { assert?, compiler?, dependency?, descriptionData?, enforce?, exclude?, generator?, include?, issuer?, issuerLayer?, layer?, loader?, mimetype?, oneOf?, options?, parser?, realResource?, resolve?, resource?, resourceFragment?, resourceQuery?, rules?, scheme?, sideEffects?, test?, type?, use? }, ...]
-> A rule.
Details:
* configuration.module.rules[3].use[0] should be one of these:
object { ident?, loader?, options? } | function | non-empty string
-> A description of an applied loader.
Details:
* configuration.module.rules[3].use[0] should be an object:
object { ident?, loader?, options? }
* configuration.module.rules[3].use[0] should be an instance of function.
* configuration.module.rules[3].use[0] should be a non-empty string.
-> A loader request.
* configuration.module.rules[3].use[0] should be one of these:
object { ident?, loader?, options? } | function | non-empty string
-> An use item.
Error: Process completed with exit code 2.
However, what I want is simply to import Katex, like
import 'katex/dist/katex.min.css'
into my table component.
Maybe someone has an idea?
Ok, I found it. And just because it was so hard for me to find the right answer online (that might be just me) - here is my solution.
I wanted to use katex in typescript React - to show a nice table here
https://0xpolygonmiden.github.io/examples/.
For that I imported into my table component
import { Fragment } from 'react';
import ReactMarkdown from 'react-markdown';
import gfm from 'remark-gfm';
import math from 'remark-math';
import katex from 'rehype-katex'
import { assemblerInstructions } from '../data/instructions';
import 'katex/dist/katex.min.css'
So katex comes with its own CSS file. Locally it runs fine and renders beautifully. However, when I tried to deploy on GitHub pages, webpack compliant that there is a loader missing for that import.
Ok. So I tried to find the right loader for that import for webpack 5.
It is simply the sass-loader and by default node-modules are excluded so I had to remove that exclude: /node_modules/, when I defined the sass-loader for CSS files. Easy, ha?
const path = require('path');
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.json$/,
use: 'json-loader',
},
{
test: /\.(js)x?$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /\.(ts)x?$/,
exclude: /node_modules|\.d\.ts$/, // this line as well
use: {
loader: 'ts-loader',
options: {
compilerOptions: {
noEmit: false, // this option will solve the issue
},
},
},
},
{
test: /\.css$/,
use:['style-loader', 'css-loader', 'sass-loader'],
},
],
},
resolve: {
extensions: ['.css', '.tsx', '.ts', '.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
Here is the full code for those who are interested.
https://github.com/0xPolygonMiden/examples/tree/main/playground

How to bundle react script that can be used in html directly like Material UI without JSX?

We are creating a react component, for some reason, we need it can be used in HTML directly. Like this one https://github.com/mui/material-ui/blob/master/examples/cdn/index.html. I am trying to use webpack to package my script, but it can't be used in HTML.
Here is my simple webpack config
const path = require('path');
module.exports = {
entry: glob.sync('./core/test/index.jsx'),
output: {
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'global',
filename: 'test.development.js',
},
devtool : 'source-map',
module: {
rules:[
{
test: /\.svg$/,
use: {
loader: 'svg-url-loader',
options: {
encoding: 'base64'
}
}
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.(css|sass|scss)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
}
]
},
mode: 'development'
};
How should I do with the script?

Prefix React className

So, I need to prefix my react components classNames so it doesn't conflict with global classes.
And no, I can't use CSS modules because of how my yarn workspaces are built
So, if I have a button class I would like it to become my_app-button
<button className="button">Hello StackOverflow</button>
to
<button className="my-app_button">Hello StackOverflow</button>
PostCSS does that for me in the CSS part, but on the React Side I wasn't able to find a solution
All of my components use typescript
I've tried using a webpack loader to do the job, and it did! But only on my Storybook server, when I used it with my separate webpack config it didn't work
The only error that is given to me is invariant 85
This is my Webpack Config, alongside the webpack loader and the babel-loader
import { Configuration, ProgressPlugin } from 'webpack'
const config: Configuration = {
mode: 'production',
entry: './src/index.ts',
target: 'node',
output: {
filename: '[name].js',
path: __dirname + '/dist',
publicPath: '',
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['#babel/preset-react', '#babel/preset-env', 'minify'],
},
},
],
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'#babel/preset-react',
'#babel/preset-env',
'#babel/preset-typescript',
'minify',
],
},
},
],
},
{
test: /\.tsx$/,
exclude: /node_modules/,
use: [
{
loader: 'react-classname-prefix-loader?prefix=giffy_css',
},
],
},
{
test: /\.(png|jpe?g|gif)$/,
exclude: /node_modules/,
use: [{ loader: 'file-loader' }],
},
],
},
plugins: [new ProgressPlugin()],
}
export default config
and just in case you need it, this is my Storybook webpack configuration
webpackFinal: async (config, { configType }) => {
config.module.rules.push({
test: /\.tsx$/,
exclude: /node_modules/,
use: [
{
loader: 'react-classname-prefix-loader?prefix=giffy_css',
},
],
})
// console.log(config.module.rules[0].use[0].options.overrides[0])
return config
}
i have also tried first compiling my components with babel and then using webpack
i tried searching for an equivalent webpack loader in babel, but could not find it

Autodesk React Forge problem with forge-dataviz-iot-react-components in a empty project

If you install the official npm package, it works.
But according to the official documentation and simply including import { Viewer } from "forge-dataviz-iot-react-components" (like in this example) in a empty new react project (using npx create-react-app) you will get this error:
./node_modules/forge-dataviz-iot-react-components/client/components/BasicTree.jsx 107:16
Module parse failed: Unexpected token (107:16)
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
| if (node.children.length > 0) {
| return (
> <TreeItem
| id={`tree-node-${node.id}`}
| key={node.id}
Which loader do I need to add on webpack to avoid this error?
it is not possible to include the package https://www.npmjs.com/package/forge-dataviz-iot-react-components inside a react project made with npx create-react-app (hoping Autodesk is going to fix this problem soon).
You need to edit /node_modules/react-scripts/config/webpack.config.js in 2 parts:
one line about PIXI
...
alias: {
'PIXI': "pixi.js/",
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
// Allows for better profiling with ReactDevTools
...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',
}),
...(modules.webpackAliases || {}),
},
...
and another part about /forge-dataviz-iot-react-component
...
module: {
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
{
test: /forge-dataviz-iot-react-component.*.jsx?$/,
use: [
{
loader: require.resolve('babel-loader'),
options: {
presets: ["#babel/react", ["#babel/env", { "targets": "defaults" }]],
plugins: ["#babel/plugin-transform-spread"]
}
},
],
exclude: path.resolve(__dirname, "node_modules", "forge-dataviz-iot-react-components", "node_modules"),
},
// TODO: Merge this config once `image/avif` is in the mime-db
// https://github.com/jshttp/mime-db
{
test: [/\.avif$/],
loader: require.resolve('url-loader'),
options: {
limit: imageInlineSizeLimit,
mimetype: 'image/avif',
name: 'static/media/[name].[hash:8].[ext]',
},
},
...
after that on /node_modules/forge-dataviz-iot-react-components/client/components/Viewer.jsx you will get errors about undefined Autodesk variable easily fixable changing Autodesk with window.Autodesk.
Although you will not see any other errors, the package will not work.
I recently tried this package and I got the same problem.
So I created a React project from scratch without CRA and followed the webpack.config.js of this repo : Forge Dataviz IOT Reference App
Here's my webpack.config.js file :
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
},
resolve: {
modules: [path.join(__dirname, 'src'), 'node_modules'],
alias: {
react: path.join(__dirname, 'node_modules', 'react'),
PIXI: path.resolve(__dirname, "node_modules/pixi.js/"),
},
},
devServer: {
port: process.env.PORT || 3000
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{ loader: "babel-loader" }
]
},
{
test: /forge-dataviz-iot-react-component.*.jsx?$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["#babel/react", ["#babel/env", { "targets": "defaults" }]],
plugins: ["#babel/plugin-transform-spread"]
}
},
],
exclude: path.resolve(__dirname, "node_modules", "forge-dataviz-iot-react-components", "node_modules"),
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
],
},
{
test: /\.svg$/i,
use: {
loader: "svg-url-loader",
options: {
// make loader to behave like url-loader, for all svg files
encoding: "base64",
},
},
},
],
},
plugins: [
new HtmlWebPackPlugin({
template: './src/index.html',
}),
],
};
Update :
If you want to use CRA, you can customise your webpack config using Customize-CRA and create a config-overrides.js like this :
/* config-overrides.js */
const path = require("path");
const {
override,
addExternalBabelPlugins,
babelInclude,
babelExclude,
addWebpackAlias
} = require("customize-cra");
module.exports = override(
babelInclude([
path.resolve("src"), // make sure you link your own source
path.resolve("node_modules")
]),
babelExclude([path.resolve("node_modules/forge-dataviz-iot-react-components/node_modules")]),
addWebpackAlias({
['PIXI']: path.resolve(__dirname, 'node_modules/pixi.js/')
})
);
I managed to make this work on a fresh CreateReactApp project, so you should be able to make it working on your project.

Multiple webpack configs, single react library chunk

I've been looking into applying microservices concepts into the front end. One of the big benefits I'm looking to achieve is independently deployable UI components/pages with my React app. I'm looking at taking a Composite Frontend approach like in this article.
Our app would likely have multiple webpack configs, one for each of the main pages/components. I want to be able to use a single React lib chunk across each of our pages. Here's an example of two possible webpack configs:
// Page 1 component
var webpack = require("webpack");
module.exports = {
entry: {
Page1: 'Page1.js',
React: ['react', 'react-dom']
},
output: {
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.css$/,
loader: 'style!css'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('React', 'bundle.react.js')
]
}
// Page 2 component
var webpack = require("webpack");
module.exports = {
entry: {
Page2a: 'Page2a.js',
Page2b: 'Page2b.js',
React: ['react', 'react-dom']
},
output: {
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.css$/,
loader: 'style!css'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('React', 'bundle.react.js')
]
}
Is this possible to reference the same common chunk across different webpack configs?

Resources