can't find less loader and rules in webpack.conf - reactjs

I want to add Less support to a React project. According to these links:
Adding SASS or LESS support to create-react-app
React + CSS Modules + LESS + Webpack 4
I ejected project and installed less and less-loader, but I can't find rules section in webpack.config.js.
Why my config file is not like the config file in these two pages? (webpack version is 4.1.0)
How can I add Less support to my project?
If I add
{
loader: require.resolve('less-loader'),
},
to file config, I'll get "Failed to compile" error
Cannot read property 'denominator' of undefined
in ...\node_modules\bootstrap\dist\css\bootstrap.css (line 2100, column 2)

Those tutorials are based on the old CRA, in the latest one they merged dev and prod files and did some additional mumbo jumbo to make it more "concise". One of those things was extracting CSS loaders into a standalone helper function - getStyleLoaders(). The rules section is below and the output of getStyleLoaders is merged onto it at an appropriate point.
In general what you are doing by appending your loader at the end of loaders array, is correct. And the error is not on the webpack side. It's rather thrown by less-loader not being able to interpret the file, because of invalid syntax inside.

Related

Using Static Assets in Elecron + React

I am new to Electron, and I have been having some trouble trying to do something simple in an Electron + React application. All I want to do is: Load a 3D model (.glb) located in my src/assets directory from a React component. I created the project using this guide. In a typical React project, I can just import the file directly in my JS module and reference the path in my code. However, with the default Webpack config, the file can't be found. There's obviously a gap in my understanding on how React + Webpack work when loading assets. What am I missing? Any help is greatly appreciated.
Thanks!
Turns out, the Webpack documentation spells out the answer clearly. Who knew? I found a lot of similar questions/answers for older versions of Webpack, so I'll post one here for Webpack 5. It requires a trivial two-line addition to the webpack.rules.js file:
{
test: /\.(png|jpg|gif|svg|glb)$/,
type: 'asset/resource'
}
The key is the asset/resource line. It's new to Webpack 5 and allows the bundling of assets without needing any additional loaders. With that, assets can be included as Javascript modules and Webpack will take care of the rest.
So, one can do:
import modelSrc from "../assets/some_awesome_model.glb";
And that's that. Webpack will spit out a URL such as /9feee593dc369764dd8c.glb, meaning Webpack has located and processed the asset.

Typescript cannot load SVG as react components

I'm trying to import in Typescript some SVG icons, but I'm facing some problems.
At the first time I tried to import them, Typescript wasn't able to recognize the file extension.
I solved this issue by creating, as suggested in other Stack Overflow and Github topics, a custom.d.ts file with this rule inside:
declare module "*.svg" {
const content: React.StatelessComponent<React.SVGAttributes<SVGElement>>;
export default content;
}
But the problems seem to not finish here, even if the compilation seems going fine.
The current project I'm working on, is structured this way:
Typescript + React package (with SVG icons files) (SDK)
React Internal Sample page (package) to use the SDK
other internal packages...
For our development phase, we build through Webpack all the packages through different loaders and see the result through the Sample page.
But the final product flow to production is quite different: I export the SDK as CommonJS to an internal NPM Registry so another company can use it in a React project (the equivalent of the Sample page but for production) and push to production the final Webpack bundles with both projects inside.
So, to load in the Sample application the SVG icons, I'm using #svgr/webpack loader, which converts the files.
But when I have to export the SDK through npx tsc, I see that the exported folder, does not contain the folders with svg files.
I've tried to include them in tsconfig.json/files, but got this error:
TS6054: File '<path>/*.svg' has an unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'.
So, to attempt exporting them I converted my exporting script to use #svgr/cli to export the files to React files from SVGs before compiling to typescript:
// package.json
scripts: {
"build-ts": "rm -rf ./lib; yarn convert-svg-to-react; npx tsc",
"convert-svg-to-react": "npx #svgr/cli -d src src --typescript",
}
In this way, I get the new Typescript files mixed with the SVGs inside the package (so I'll have to remove them later) and I can see them in the exported folder lib.
But watching inside the Typescript exported code, I can see this line (for each svg import):
var close_svg_1 = __importDefault(require("./icons/close.svg"));
Leaving out the Typescript function for Babel __importDefault, you can see that it still requires the file svg, but what I have at this point, are the React components that replaces those files.
During development it works fine because #svgr/webpack loader, resolves the svg files.
But requiring svg files that do not exist, should make the application above it crash.
So, I'm stuck and I need some clues to get out of this situation.
Some clues that I got (but wasn't able to find how to do that), were:
[Best] Find how I can export raw svg files as they are during Typescript compilation without doing that manually, as they are not all in one folder but divided per components areas in the package tree. Doing this, I would tell the other company to add #svgr/webpack to its own building process.
Find how can I tell Typescript to import svg files without specify the extension (currently, removing .svg probably makes it fallback to .ts/tsx and therefore it cannot find the file with that name). In this way, the require would keep requiring the same file name but I could convert SVG to React Components without occurring in problems. But this would also require Typescript to export the file
Otherwise, I should convert all the SVGs in React components and directly use them instead of making them being compiled by #svgr/webpack, but I'm not sure this would have some other side-effects.
Any other clues or any way to achieve the ideas I got? Thank you everybody.

Transpiling React code, but leave ES6 alone

I'm writing a Chrome Extension using ES6/React/Redux babel and Gulp.
I was using babel presets es2015, stage-2 and react.
Then I realized as I'm only targeting Chrome I could get rid of the es2015/estage-2 stage as it's supported by Chrome.
So the first I tried was to get the .babelrc and remove the references to es2015 and stage-2.
Not so fast... Before running webpack gulp script fails to run.
What I tried first was to make only the gulp file ES5 compatible.
Then I got errors of spread operators not being supported, so I re-added "stage-2" loader.
Then I got errors in different modules:
> WARNING in ./background/src/index.html Module parse failed:
> /my_path/my_project/src/index.html Unexpected token (1:0) You may need
> an appropriate loader to handle this file type. SyntaxError:
> Unexpected token (1:0)
> at Parser.pp$4.raise (/my_path/my_project/node_modules/acorn/dist/acorn.js:2221:15)
To help to understand how my code is structured, it's in 3 main folders:
background, content and popup. Each one representing a Chrome environment.
For each one, I have a separated webpack.config.js file, similar to this one: https://pastebin.com/hseVyQaw
Gulp then calls webpack for each config file and generated the bundle output file for each one, during the build process.
There's a way to make Gulp/Webpack works with ES6 syntax, while not transpiling it for the deployment?
What's the best approach for this issue?
Gulp version
> [17:32:27] Requiring external module babel-register
> [17:32:27]CLI version 3.9.1
> [17:32:27] Local version 3.9.1
Webpack version: 1.14.0
UPDATE
After adding html-loader as suggested by #Michael Jungo it seems to run fine, but it gives me a warning, not sure how bad is to ignore it:
WARNING in ./background/src/index.js
Critical dependencies:
17:29-52 the request of a dependency is an expression
# ./background/src/index.js 17:29-52
UPDATE 2
Oh, Chrome is complaining about modules syntax of my extension, but based on what I read it's suppose to be supported:
Uncaught SyntaxError: Unexpected token import
Your error is not related to babel or any ES6 features. You're trying to import the HTML file ./background/src/index.html but in the config you've posted, there is no rule for .html that could handle these files, therefore webpack tells you that you might need an appropriate loader for this file type.
You can use the html-loader and add the following rule to your loaders array:
{
test: /\.html$/,
loader: 'html-loader'
}
As for your babel config, it should work as you wanted. Keep in mind if you're using ES modules (import/export) you would still need to transpile them or switch to webpack 2 which supports them out of the box. Also UglifyJs doesn't understand ES6 syntax, if you want to uglify ES6 you have to use an alternative like babili with the babili-webpack-plugin.

how to confirm if tree shaking is working with Webpack 2?

I just finished updating my react app from webpack 1 to webpack 2. However, My bundle size increased by ~30Kb. I was hoping the bundle size would decrease , How can i confirm that tree shaking worked. And why the increase?
TL;DR: As of version 2.3, webpack doesn't have any tree shaking. It merely uses UglifyJS to remove unused code.
First we must define what is tree shaking.
Stackoverflow defines it as "dead code elimination algorithm for modern javascript".
Webpack clarifies that it relies on ES2015 module import/export for the static structure of its module system.
Rollup (which originally popularized the term) also has a similar explanation.
So we can deduce a specific definition: static exclusion of unused ES module exports.
Now, let's see which stages of transformation each module usually has:
babel-loader is fed an entry point which is a javascript file in some module format. Babel can either transform it to another module format of leave it as is (module: false)
webpack will statically parse the file and find imported modules (using some sort of regexp)
webpack will either transform the module format (if babel doesn't tranform it already) or add some wrappers (for commonjs modules)
imported module becomes an entry point and goes to babel-loader
after all modules are loaded and transformed, uglify will process the result bunle and remove unused code (unused: true)
Now, we can see that while uglify can remove unused exports it doesn't actually rely on ES module syntax. It's just a general purpose dead code elimination so it can't be defined as "Tree shaking".
So how can we confirm whether webpack has tree shaking?
First of all, all the code must be in ES module format.
As it was already mentioned in another answer we must disable Uglify.
We also must disable babel's module transformation as we can't know whether a module is used at that phase.
And now if webpack actually implements a tree shaking algorithm we can confirm it by looking at the bundle size of this entry point:
import { keyBy } from 'lodash-es'; // lodash is in ES module format
console.log(keyBy([{ key: 'value' }], 'key'));
If webpack does have tree shaking the result should be tens of kilobytes. If it doesn't it would be half a megabyte or more.
There's a few steps you can take:
turn off minification/uglify
insert comments into some of the functions you know are unused
check the output to see if those comments are there or not
Webpack can also show you the size of each import module/package. For example, at work we have a bundle that includes both lodash and underscore because one of the libraries we're using requires each of them; webpack clearly shows how many kilobytes each package adds:
You should be able to see from this where the increase in size is coming from.

Redux + React understanding size of webpack bundle

I read various answers like this regarding reducing the bundle size. First I wanted to understand what is going on with my 13MB bundle, so I've installed the Webpack Bundle Size Analyzer. It generated this output:
There are a few things I don't understand:
Why is react appearing twice with two different sizes?
How come it doesn't add up to 13MB? (it's around 4.75MB)
Does this means my own code is only 4KB? (last line)
Is it possible to understand from this tree how to save KBs? I mean, for instance, is there a way to use only parts of lodash or is it required as a whole by React?
Update:
Found the answer for #1. React-color requires ReactCSS which requires a different version of react and lodash. Without React-color my bundle dropped from 13MB to 8MB. I will try to find a way to re-use react and lodash.
It seems that you have eval-source-map devtool option in your webpack config file, that's why you are getting such huge bundle size. This way, sourcemaps are putting inside of bundle js file.
Try to change this option to source-map to build source maps separately.
// webpack.config.js
{
devtool: 'source-map',
// ... other options
}
1) Sometimes other dependencies require again lodash and / or react, so these dependencies are pulled in multiple times by webpack (usually happens when the version differs), here the DedupePlugin should help. The final bundle size also depends on your devtool entry in webpack, so in development it is usually much bigger as you probably have sourcemaps enabled and a your build is not optimized. Hard to tell without your actual webpack config.
2) Maybe webpack is already deduping?
3) last line says 400KB
4) You can definitely save a few kbs. A good way to start is using lodash-webpack-plugin with babel-plugin-lodash, this will import only the lodash methods you are actually using instead of the complete library. You can also just require the bits you need from material-ui as outlined in this answer.
I recently added the webpack-dashboard to my webpack config, so I instantly see the output and percentages that make up my bundle size, so maybe you think twice about adding huge dependencies if you don't really need them.

Resources