Correction made to my previous Question (Instead ofObfuscate, I'm actually trying to find out Uglify)
I've recently began to work on ReactJS, while I try to bundle to code in production.. I realized all the codes are in readable format. I'm aware of the approach below:
Set false to generate sourcemap.
However, this way my code is still kind of readable in terms of my
API Endpoint
API keys
I'm trying to understand what is the proper way to fully uglify my code in production?
Here's how we can UGLIFY our code in ReactJS.
Run npm run eject
Run npm install uglifyjs-webpack-plugin --save-dev
Go to webpack.config.js at your config folder (you should get this folder after eject)
Search for keyword minimize: isEnvProduction then append the following at bottom
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
warnings: false,
parse: {},
compress: {},
mangle: true, // Note `mangle.properties` is `false` by default.
output: null,
toplevel: false,
nameCache: null,
ie8: false,
keep_fnames: false,
},
}),
],
If you now build your app npm run build for production environment, and you shall not see your code via Inspect Element.
Related
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. :)
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.
TL;DR
minifyify (the Browserify plugin) makes use of uglify-js but appears to be unable to handle Conditional compilation:
compression works
uglifyjs alone works for conditional compilation
minifyify provides additional compilation optimization but I have been unable to use conditional compilation with it
I'm using Browserify with the babelify transformer and the minifyify plugin. Here is the cmd, broken down in readable parts:
browserify
src/scripts/app/index.js
-o
build/prod/public/assets/js/appBundle.min.js
-t
[ babelify --presets [ es2015 ] ]
-p
[ minifyify --no-map --uglify [ --compress [ --drop_console --dead_code --conditionals --unused --if_return ] --mangle --screw-ie8 --define [ DEBUG=falseĀ ] ] ]
I've gotten every setting/option to work. However, I am unable to get conditional compilation to work.
Minifyify uses uglifyjs' minify method. The fact I'm passing by minifyify shouldn't really change anything.
Building directly through uglifyjs works
uglifyjs input.js --compress --dead_code --define DEBUG=false -o output.js
But then I lose the additional compressions/optimizations provided by minifyify.
I'm also open to another build process. My needs are resumed in the settings of the current process:
CommonJS required modules
transpiling of ES6 to ES5
advanced minification/compression
It turns out that the culprit was, more or less, uglifyjs. The property name for global definition in the task is different between CMD and Programmatic API.
cmd line: --define VARNAME=VALUE
programmatic: compress: {global_defs: { varname: value } }
That being said, it also seems that minifyify or browserify isn't passing the cmd-line options properly for global definitions - we're still investigating this
programmatic solution
By using the Browserify & minifyify programmatic API, the build task works. Below is the same task as the one in OP, but it works:
"use strict";
var browserify = require("browserify"),
fs = require("fs");
browserify("src/scripts/app/index.js")
.transform("babelify", {presets: ["es2015"], plugins: ["transform-object-assign"]})
.plugin("minifyify", {map: false, uglify: {
compress: {
drop_console: true,
dead_code: true,
conditionals: true,
unused: true,
if_return: true,
global_defs: {
DEBUG: false
}
},
mangle: true,
"screw-ie8": true
}})
.bundle()
.pipe(fs.createWriteStream("build/prod/public/assets/js/appBundle.js"));
update in uglifyjs docs
I've proposed a modification to the current uglifyjs docs, providing an example using the programmatic API as above, so as to avoid this issue for others in the future.
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.
I'm using grunt and requirejs to build and compress our app for deployment. We want to create source maps that can be used for debugging client code remotely, without exposing our uncompressed dev code. In the requirejs options, I'm specifying for optimization:
options: {
// ...,
optimize: 'uglify2',
// ...,
uglify2: {
mangle: {
except: ['jQuery']
},
compress: {
sequences: true,
booleans: true,
evaluate: true,
conditionals: true,
comparisons: true
}
},
generateSourceMaps: true,
preserveLicenseComments: false,
// ...
}
This generates sourcemaps as expected, but they include the source files inside the maps, which increases file size significantly and exposes our original dev code. UglifyJS2 docs says it supports a sourceMapIncludeSources option which I've tried passing in an options object in the uglifyjs2 config, but it does not appear to be respected here. Is there a way to get requirejs to generate source maps without including the sources, or to get it to respect the options passed to uglify?