Minify css from webpack's ExtractTextPlugin and style-loader - javascript

In this tracer repo: https://github.com/pconerly/libsass-spritesmith-webpack-tracer
And this line:
https://github.com/pconerly/libsass-spritesmith-webpack-tracer/blob/master/webpack.config.js#L82
I'm loading .scss, and extracting them into plaintext. I'd also like to minify them--- how do I do that? style-loader doesn't seem to have an option for it. Should I be using another plugin like css-loader instead?

So this will come automatically through the CSS loader unless you've explicitly disabled it. Since you're asking the question I'm assuming this means you have. The UglifyJsPlugin won't minify the CSS by itself if you're extracting and not minifying.
For my needs I needed to extract the CSS and then provide both a minified and non-minified version. So I ran into the same problem where I could have it minified or non-minified but not both.
I was able to get this to work using the optimize-css-assets plugin for Webpack. It will allow you to minify the CSS you've extracted using ExtractTextPlugin and you can set a RegEx rule similar to the UglifyJsPlugin settings.
By default this plugin uses the css-nano module for it's compression, although you can swap out to your preferred module if you wish.
Here's a basic configuration:
plugins: [
new ExtractTextPlugin('[name].css'),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false },
include: /\.min\.js$/
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.min\.css$/,
cssProcessorOptions: { discardComments: { removeAll: true } }
})
]

I would suggest looking at postcss and postcss-loader. That way once you have it set up you can do lots of cool stuff to you CSS/SCSS without having to spend days fighting webpack first.

Adding an entry for the UglifyJsPlugin works for me.
plugins: [
new ExtractTextPlugin("[name].css"),
new webpack.optimize.UglifyJsPlugin({
compressor: { warnings: false }
})
]

Related

Module not found: Error: You attempted to import babel-preset which falls outside of the project src/ directory

I'm developing an application created using create-react-app
But then I needed to use mediainfojs library, this library requires wasm files, and based on what I understood I couldn't add it using create-react-app, I had to eject it.
After ejecting it, I went to mediainfo information on how to add the wasm on the webpack
They use the CopyPlugin, but then when I tried to do that it complained about the versions of my webpack (4) and the CopyPlugin.... so, I decided to migrate to webpack 5
That is when the pain starts... after follow their migration tutorial and do a bunch of modifications on my webpack.config I got to the following error while runing yarn build:
Module not found: Error: You attempted to import /MyWorkspace/project/node_modules/babel-preset-react-app/node_modules/#babel/runtime/helpers/esm/asyncToGenerator which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
The only place calling this babel-preset-react-app are in the configuation
Here:
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve("babel-loader"),
options: {
customize: require.resolve(
"babel-preset-react-app/webpack-overrides"
),
And here:
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve("babel-loader"),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve("babel-preset-react-app/dependencies"),
{ helpers: true },
],
],
cacheDirectory: true,
cacheCompression: isEnvProduction,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
I have looked into similar issues reported here, but mostly of them seem to be related to either static files being dynamically imported or imports referencing ".." dir after the project directory
The full webpack config file is here
I'm probably missing something very silly, I'd be glad if someone can point it out.
I had a similar challenge and I was able to fix this by adding these definitions at the top of my webpack.config file
const babelRuntimeEntry = require.resolve('babel-preset-react-app');
const babelRuntimeEntryHelpers = require.resolve(
'#babel/runtime/helpers/esm/assertThisInitialized',
{ paths: [babelRuntimeEntry] }
);
const babelRuntimeRegenerator = require.resolve('#babel/runtime/regenerator', {
paths: [babelRuntimeEntry]
});
Then where you have the ModuleScopePlugin in the resolve.plugins
update it to be
new ModuleScopePlugin(paths.appSrc, [
paths.appPackageJson,
babelRuntimeEntry,
babelRuntimeEntryHelpers,
babelRuntimeRegenerator])
I'm also attempting to upgrade an ejected CRA project to Webpack 5. I was able to move forward using babel-preset-react-app-webpack-5, only to encounter the next CRA-related issue.
Be sure to replace calls like require.resolve("babel-preset-react-app/dependencies") with require.resolve("babel-preset-react-app-webpack-5/dependencies").
Also, be aware the package does not appear to be production-ready, but my own project is still in early development.
I had this issue with a few other babel packages after trying to upgrade an ejected CRA app to webpack v5. I tried many different approaches some of which worked in dev but not in prod and vice versa. I found this comment in the storybook github and it was the only thing that that seemed to work in all scenarios for me.
It's kinda annoying, but by simply moving the offending packages from devDependencies in my package.json to dependencies, it seems to fix the issue. I could spend more time trying to figure out why that fixes it, but I'll leave that to someone with more free time. :)

how can i optimize main.js size?

I have a Vue.JS application. I'm creating it using webpack. I'm getting large main.js size after application build. (9 Mb).
I have any packages. But I'm getting large main.js size after creating empty application, too. How can I solve the issue?
You can use uglifyjs-webpack-plugin, compression-webpack-plugin to optimize your bundle size.
Here is my full code
Example:
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: false,
extractComments: 'all',
uglifyOptions: {
compress: true,
output: null
}
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
safe: true,
discardComments: {
removeAll: true,
},
},
})
]
},
plugins: [
new CompressionPlugin({
test: /\.(js|css)/
}),
new UglifyJsPlugin()
],
So UglifyJsPlugin help you to minify and uglify js code. CompressionPlugin help you compress your js file into gzip extension.
But I would recommend you use webpack-bundle-analyzer to check the bundle size affect final build
There are several ways of optimising your main file and application performance. All of them works, but some might be not suitable for your case.
Code splitting and lazy load components.
In my case I solved problem by using code splitting and lazy loading components.
It turned main.js into very small one and other files are loaded when becomes visible.
Check this article about code splitting and this one about lazy load in Vue.js. It is very common approach in libraries like React or Vue.js.
Optimising libraries. No much to add, check if is possible to pull function instead of whole library (like in lodash) or use native implementation instead of external one. Unused npms also does not makes your main file lighter. Even if now you don't have much packages optimising them in future will make a difference.
Minifying and uglyfing. Most common and most important practice not only because of performance reasons but also because of security. Those two webpack plugins are the most popular webpack plugins at all:
UglifyJS - for JS
MiniCSS - for CSS
Webpack optimisation. Webpack knows thousands of tricks how to make your bundle smaller, but from time to time it might be your false friend. Remember to define how to resolve some dependencies, for example by pointing to proper node_module or library. It can looks like, where alias for lodash saved me a lot of kBs.
resolve: {
modules: ['app', 'node_modules'],
extensions: [
'.js',
],
mainFields: [
'browser',
],
alias: {
'lodash-es': 'lodash'
}
}
Code responsibly.
The best way to understand your problem is to analyse main.js, you can achieve it by adding Bundle Analyzer Plugin to your webpack config into plugins. It should looks like:
new BundleAnalyzerPlugin({
reportFilename: 'report.html',
analyzerMode: 'static'
})
Look into docs if you want to adjust it somehow. Interactive report is awesome.

Define css files within webpack.config.js

I am building something like a static website generator that uses webpack to build the project and create a bundle with it.
In this project, a user is able to specify custom css files. I want those css files to be bundled with the final result. The issue is, that I do not have the paths to those css files available during development, so I can't do import 'some-asset-file-provided-by-the-user.css' in the javascript code that is going to be bundled. But I have them available when calling webpack.compile(config).
I am looking for a way to inject those css files into the bundle. So far I tried various ways, such as:
const stylesheet = 'some-asset-file-provided-by-the-user.css'
require(stylesheet)
Which did not work, probably because webpack is not able to deal with this "dynamic" require. Then I used the webpack define plugin for this
/* webpack.config.js */
new webpack.DefinePlugin({
stylesheet: 'some-asset-file-provided-by-the-user.css'
}),
/* app.js */
require(stylesheet) // should be replaced by the webpack define plugin with 'some-asset-file-provided-by-the-user.css'
which also did not work. I also tried to find a way to do something like this:
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(Object.assign({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
useFiles: ['file-a.css', 'file-b.css']
}
}
]
}, extractTextPluginOptions))
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
which also failed because apparently neither style-loader nor css-loader support this type of interaction.
How can I solve this? I am open to writing a plugin for this, but I'd rather use something existing.
The simplest way to include the CSS is by adding it to your entry point. To make this easier, you should use an array as entry point even if it's just a single file, so you can simply push the CSS.
For example:
entry: {
app: ['./src/index.js'],
// Other entries
},
In your compile script you add it to entry.app before passing it to webpack.
config.entry.app.push('./user.css');
const compiler = webpack(config);

Why is style-loader used as a fallback with Webpack's ExtractSass plugin?

In the following example (found here), style-loader is being used as a fallback in development mode. Why?
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const extractSass = new ExtractTextPlugin({
filename: "[name].[contenthash].css",
disable: process.env.NODE_ENV === "development"
});
module.exports = {
...
module: {
rules: [{
test: /\.scss$/,
use: extractSass.extract({
use: [{
loader: "css-loader"
}, {
loader: "sass-loader"
}],
// use style-loader in development
fallback: "style-loader"
})
}]
},
plugins: [
extractSass
]
};
Extracting CSS from JavaScript is primarily used to not having to rely on your JavaScript (the bundle) being fully loaded until it injects the CSS into the head as a <style> tag, which can cause Flash of unstyled content (FOUC). And of course it's also used to simply separate CSS from JavaScript because that's generally preferred and allows being cached separately.
In development this doesn't really matter since the FOUC is basically a performance issue, just like the load time of your JavaScript, which hopefully you don't uglify in development either. This is neither your main concern nor representative in development mode. Besides being an additional compilation step, extracting CSS also imposes some drawbacks. For instance you lose hot reloading, because the newly compiled bundle didn't change as the content of the CSS has been extracted. The advantages are mostly aspects you care about for production and the drawbacks negatively affect development. See also Usage - Advantages and Caveats.
To be clear, the fallback is used after the configured loaders have been applied, it's just an extra step to be able to inject the CSS into a <style> tag from your JavaScript, which is the only thing that style-loader does. The fallback is used instead of extracting it to a file.

React.js app.js file size

I created pretty simple react application containing 7 pages and 13 components.
I am using gulp to compile it, browserify for dependencies, all files are minimized.
My build'ed app.js file has 1.1 MB. I think it is quite big.
What can I do to reduce its size ?
Are there any good practices to achieve smallest size ?
EDIT:
My source code without dependencies is 91 KB.
Using webpack-uglify and disabling source maps can greatly improve the output to a reasonable size (~140kbs for a hello world application)
A couple of steps:
Setting devtool in webpack config to cheap-source-map or cheap-module-source-map so the source maps are not bundled with the output:
{
eval: 'cheap-source-map'
}
Activate uglify plugin or call webpack with -p argument
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
Defining node environment for production causes webpack to remove test helpers and optimize the ouput size:
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
},
})
]
Note: these steps should be only used for production builds as they increase the build time.
Resource:
https://medium.com/modus-create-front-end-development/optimizing-webpack-production-build-for-react-es6-apps-a637e5692aea#.bug2p64de
It seems to me that you just have a lot of dependencies. In modern JS development it's pretty easy to go overboard and include every library under the sun. People tend to forget that if they include library X, they also include its dependencies.
The only thing I can recommend is to go through each one and asses, how useful it actually is. If you're just using a small part of a big library, try to replace it with something smaller, or maybe roll out your own solution.
Maybe you can find replacements here: microjs
Here is my plugin code that reduced the js from about 3MB to 350KB:
plugins: debug ? [] : [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"production"'
}
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
mangle: true,
sourcemap: false,
compress: {
warnings: false,
}
}),
]
Have a look at browserify-shim. That library helps you to outsource your dependencies with CDNs. So you have less dependencies in your compiled file.
If you don't manage to get your bundle size to where you need it to be, code-splitting is also an option.
The idea is that you don't send the code for your entire app at once - you send what is needed to load the first page, and then lazy-load other app code as it's needed.

Categories