{"resource":{"author":{"id":"h2so9H8jPMmgUKGghoNl","name":"Yomesh Gupta","username":"yomeshgupta"},"content":{"link":"https://devtools.tech/optimizing-js-bundle-define-plugin-webpack/","difficulty":2,"domain":1,"type":1,"isInternal":true,"body":"In this blog post, we are going to talk about webpack's DefinePlugin and how we can use it to optimize our JS bundle by conditionally loading modules when required.\n\n### What is DefinePlugin?\n\nDefinePlugin is provided out of the box by Webpack. As per the [documentation](https://webpack.js.org/plugins/define-plugin/#root \"Define Plugin Webpack\"),\n\n> The DefinePlugin allows you to create global constants which can be configured at compile time.\n\nIn our webpack config --\n\n```js\nnew webpack.DefinePlugin({\n  PRODUCTION: JSON.stringify(true),\n});\n```\n\nHere we define a constant which would be available in the global scope of our bundle --\n\n```js\nif (PRODUCTION) {\n  console.log(\"Production\");\n}\n```\n\nWebpack will replace the `PRODUCTION` constant with the value provided in the config (here it is `true`). Every key provided in the DefinePlugin will be available as a constant in the bundle and values must be converted to `string` or webpack will stringify them for you. You can read more about the rules of identifiers [here](https://webpack.js.org/plugins/define-plugin/#usage \"DefinePlugin Usage\").\n\n### Problem Usecase\n\nMost of the time, we set up our build process and ship a bundle to the client. We can do all sorts of fancy things like `code-splitting`, `tree shaking`, `uglification` and much more. However, we might ship features that aren't required or might be experimental. In such cases, we are pushing more code than required and unnecessarily increasing our bundle size. Here, `DefinePlugin` really shines and can help us avoid such circumstances.\n\nFor demo purposes, let's consider a use-case. Suppose, our system offers the following features\n\n1. Reporting\n2. Payment\n3. A/B Testing\n4. Click Tracking\n5. Experimental Feature 1\n6. Experimental Feature 2\n\nHowever, not all our clients need all the features. Different clients might require different feature sets depending upon their needs, such as\n\n1. Client A might require Reporting and Payment features.\n2. Client B might require Reporting, A/B Testing, Click Tracking.\n3. Client C might want to try Experimental Features.\n4. Client D might require all features.\n\nHence, we can't ship all the features to all the clients. Doing so, would not be the optimal solution. Let's see how DefinePlugin can help us in solving this problem.\n\n#### Code Time\n\nYou can find code for this blog post [here](https://github.com/yomeshgupta/webpack-define-plugin \"Webpack Define Plugin Blogpost Github Repo\").\n\nWe will set up the following directory structure\n\n```bash\n|-- dist\n  |-- bundle.js\n|-- src\n  |-- configs\n    |-- config-1.json\n    |-- config-2.json\n    |-- config-3.json\n    ..\n  |-- index.js\n|-- main.js\n```\n\nLet's briefly look at what some of the file/folders means\n\n1. main.js\n\n   - This file will contain all the code which would be required for generating our bundle.\n\n2. src\n\n   - This folder will contain all the client-side code we need to ship.\n\n3. src/index.js\n\n   - This file will be the entry point to our bundle.\n\n4. configs\n\n   - This folder will contain all the config files which will dictate what goes into our bundle and what not.\n\nCreate a `config-1.json` which would allow us to include all the modules in our bundle. Different config files will dictate different bundles depending upon which features are enabled or disabled.\n\n```\ncd src/configs\ntouch config-1.json\n```\n\nPut the following code in the file\n\n```js\n{\n  \"REPORTING\": true,\n  \"PAYMENT\": true,\n  \"A_B_TESTING\": true,\n  \"CLICK_TRACING\": true,\n  \"EXPERIMENTAL_FEATURE_1\": true,\n  \"EXPERIMENTAL_FEATURE_2\": true\n}\n```\n\nLet's start working on our bundle generation. We are going to run webpack as part of a node service which would be a little different from traditionally creating `webpack.config.js` and running the `webpack` command from CLI.\n\nOpen the `main.js` and paste the following code:\n\n```js\nconst Promise = require('bluebird');\nconst path = require('path');\nconst fs = Promise.promisifyAll(require('fs'));\nconst webpack = require('webpack');\n\nconst CONFIG_PATH = './src/configs/config-1.json';\n\nfunction init() {\n  return fs\n    .readFileAsync(CONFIG_PATH, { encoding: 'utf8' })\n    .then(content => {\n      ...\n    });\n}\ninit();\n```\n\nIn the above code snippet, we are setting up all the required dependencies and reading the config file. This config file can be generated via any means as reading from a DB or some pre-processing mechanism that generates this config.\n\nNow, let's look at our webpack config\n\n```js{8,24,28}\n  ...\n  .then(content => {\n      return new Promise((resolve, reject) => {\n        const config = JSON.parse(content);\n\n        if (!config || !Object.keys(config).length) return reject('Empty Config Found');\n\n        const compiler = webpack({\n          mode: 'production',\n          entry: path.join(__dirname, 'src', 'index.js'),\n          output: {\n            path: path.join(__dirname, 'dist'),\n            filename: 'bundle.js'\n          },\n          module: {\n            rules: [\n              {\n                test: /.jsx?$/,\n                loader: 'babel-loader',\n                exclude: /node_modules/\n              }\n            ]\n          },\n          plugins: [new webpack.DefinePlugin({ ...config })]\n        });\n        new webpack.ProgressPlugin().apply(compiler);\n        compiler.run(err => {\n          return err ? reject(err) : resolve();\n        });\n      });\n  })\n  ...\n```\n\nIn the above code snippet,\n\n1. We are creating a `compiler` instance bypassing the standard config to the webpack function.\n2. We are defining global constants as discussed earlier by using the `DefinePlugin` and passing the config read using file interface ('/configs/config-1.json').\n3. We are applying `ProgressPlugin` to our webpack instance to get a clear picture of what's happening and then calling the `run` method to start bundle creation.\n4. Run method takes a callback that has two parameters `error` and `stats`. We are only concerned with the `error` parameter for now.\n5. If any error occurs while creating the bundle then we will reject else resolve the promise.\n\nWe are here using the Node.js API provided by webpack. It is useful in our scenario as it provides us with more granular control over the build pipeline. You can read more about the Webpack's Node Interface [here](https://webpack.js.org/api/node/#root \"Webpack Node Interface\").\n\nNow, I am going to add dummy modules so that we can run our build pipeline and compare the results. You can check out the [github repo](https://github.com/yomeshgupta/webpack-define-plugin \"Webpack Define Plugin Blogpost Github Repo\") for the code.\n\n#### Conditional Module Loading\n\nOpen the `src/index.js` and paste the following code\n\n```js\nvar reporting;\nvar payment;\nvar abTesting;\nvar clickTracking;\nvar experimentalFeature1;\nvar experimentalFeature2;\n\nif (REPORTING) {\n  reporting = require(\"./reporting.js\");\n}\nif (PAYMENT) {\n  payment = require(\"./payment.js\");\n}\nif (A_B_TESTING) {\n  abTesting = require(\"./abTesting.js\");\n}\nif (CLICK_TRACING) {\n  clickTracking = require(\"./clickTracking.js\");\n}\nif (EXPERIMENTAL_FEATURE_1) {\n  experimentalFeature1 = require(\"./experimental-1.js\");\n}\nif (EXPERIMENTAL_FEATURE_2) {\n  experimentalFeature2 = require(\"./experimental-2.js\");\n}\n```\n\nIn the above code snippet, variables like `REPORTING`, `PAYMENT`, `CLICK_TRACKING` would be available to us in our bundle at compile time. Webpack will replace them with the values provided via `DefinePlugin`. Using these values, we are going to conditionally load our modules.\n\nAfter adding dummy modules, let us build our bundle(s) and compare the results!\n\n```js\nabTesting.js    267 KB\nclickTracking.js  107 KB\nexperimental-1.js 54 KB\nexperimental-2.js 54 KB\npayment.js      214 KB\nreporting.js    161 KB\n```\n\n1. Config 1 -- All modules enabled\n\n```js\n{\n  \"REPORTING\": true,\n  \"PAYMENT\": true,\n  \"A_B_TESTING\": true,\n  \"CLICK_TRACING\": true,\n  \"EXPERIMENTAL_FEATURE_1\": true,\n  \"EXPERIMENTAL_FEATURE_2\": true\n}\n\nbundle.js     910 KB\n```\n\n2. Config 2 -- Reporting, Payment and Experimental Feature 1 enabled\n\n```js\n{\n  \"REPORTING\": true,\n  \"PAYMENT\": true,\n  \"A_B_TESTING\": false,\n  \"CLICK_TRACING\": false,\n  \"EXPERIMENTAL_FEATURE_1\": false,\n  \"EXPERIMENTAL_FEATURE_2\": true\n}\n\nbundle.js 457 KB\nReduction in size - 49.78%\n```\n\n3. Config 3 -- A/B testing, Click Tracking, Experimental Feature 1 and Experimental Feature 2 enabled\n\n```js\n{\n  \"REPORTING\": false,\n  \"PAYMENT\": false,\n  \"A_B_TESTING\": true,\n  \"CLICK_TRACING\": true,\n  \"EXPERIMENTAL_FEATURE_1\": true,\n  \"EXPERIMENTAL_FEATURE_2\": true\n}\n\nbundle.js 514 KB\nReduction in size - 43.52%\n```\n\nSo, after comparing these results now we can see how `DefinePlugin` can help us in optimizing our JS bundle and provide more granular control over what goes into the bundle and what not.\n\nYou can find code for this blog post [here](https://github.com/yomeshgupta/webpack-define-plugin \"Webpack Define Plugin Blogpost Github Repo\").\n","languages":[],"editorConfig":{}},"stats":{"views":10952,"used":0,"likes":0},"description":"","published":true,"isActive":true,"tags":["node.js","javascript","frontend","frontend fundamentals","js fundamentals","interview questions","backend","backend fundamentals","webpack","inside webpack","advanced webpack","webpack defineplugin","define plugin"],"slug":"optimizing-your-javascript-bundle-or-defineplugin-webpack---rid---kvP5tP0G6isd86ALJUeh","isPremium":false,"categories":[],"requires":[],"_id":"5f1dd7cbcbec5f7ffc0c2fae","title":"Optimizing your JavaScript Bundle | DefinePlugin Webpack","resourceId":"kvP5tP0G6isd86ALJUeh","createdAt":1595791307402,"modifiedAt":1643031859095},"currentUser":null,"isOwner":false,"recommendations":{"questions":[{"_id":"63a97bea22480f26b3cbb6ad","content":{"languages":["javascript","typescript"],"difficulty":1},"tags":["javascript","problem solving","interview question","interview preparation","function","toggle functionality","vanilla js","basic js","facebook interview","netflix","google interview questions","frontend interview questions"],"slug":"how-to-create-a-toggle-function-in-javascript-or-frontend-problem-solving-or-javascript-interview-question---qid---gbucvY2jab4e1q6Fo6iV","title":"How to create a toggle function in JavaScript? | Frontend Problem Solving | JavaScript Interview Question","questionId":"gbucvY2jab4e1q6Fo6iV"},{"_id":"6255355d3ecc8e41a79f0832","content":{"languages":["javascript","typescript"],"difficulty":1},"tags":["","javascript","format","currency","frontend","interview","problem solving","leetcode"],"slug":"implement-a-currency-formatting-utility-or-javascript-interview-question---qid---ZWwL12l2uqKZOQSAGvrl","title":"Implement a Currency Formatting Utility | JavaScript Interview Question","questionId":"ZWwL12l2uqKZOQSAGvrl"},{"_id":"6412d7afc809473cf30b5449","content":{"languages":["react","html"],"difficulty":2},"tags":["javascript","frontend","interview","razorpay.ui","ux","coding questions","tooling","devtools","notifications","fintech","product companies","top interview questions","codedamn","egghead"],"slug":"how-to-create-notifications-list-component-razorpay-interview-question-or-react-js-or-javascript-or-html-or-css---qid---nxBpTX41Lx09DQjJCa6w","title":"How to create Notifications List Component? Razorpay Interview Question | React.js | JavaScript | HTML | CSS","questionId":"nxBpTX41Lx09DQjJCa6w"},{"_id":"65e056c84e1bed1edc051089","content":{"languages":["javascript","typescript"],"difficulty":1},"tags":["javascript","frontend","code","ui","ux","problem solving","js","dsa","programming"],"slug":"how-to-find-the-most-frequent-word-in-a-paragraph-frontend-problem-solving-or-javascript---qid---vigN3iSuX6Vj6JSpzXvC","title":"How to find the most frequent word in a paragraph? Frontend Problem Solving | JavaScript ","questionId":"vigN3iSuX6Vj6JSpzXvC"},{"_id":"665a0a78407950304701413c","content":{"languages":["react","html"],"difficulty":1},"tags":["javascript","array","uber","ui","ux","frontend interview question","frontend coding challenge","devtools tech","blog","tutorial","uber questions"],"slug":"how-to-create-an-grid-lights-interactive-shape-uber-frontend-interview-question-or-javascript-or-react-js---qid---6FVH1ZMWMXd4uZ8WAGEi","title":"How to create an grid lights interactive shape? Uber Frontend Interview Question | JavaScript | React.js","questionId":"6FVH1ZMWMXd4uZ8WAGEi"}],"resources":[{"_id":"5f1dedf2cbec5f7ffc0c2fb3","content":{"difficulty":2,"domain":2,"type":1,"isInternal":true,"languages":[]},"tags":["node.js","javascript","temporal dead zone","js tdz","es6","scoping","js","advanced javascript","advanced frontend"],"slug":"understanding-temporal-dead-zone-in-javascript---rid---0KEfke08AOZuNXVxRXPW","title":"Understanding Temporal Dead Zone in JavaScript","resourceId":"0KEfke08AOZuNXVxRXPW"},{"_id":"652d6531d5ab2876a4ca214e","content":{"difficulty":4,"domain":2,"type":1,"isInternal":false,"languages":["undefined"]},"tags":["frontend interview","amazon","medium","devtools tech","blog","article","tutorial"],"slug":"how-was-my-frontend-engineer-interview-experience-amazon---rid---fProKruFZQXgvEgZffz9","title":"How was my Frontend Engineer Interview Experience @ Amazon","resourceId":"fProKruFZQXgvEgZffz9"},{"_id":"627f3614d3473370ac359796","content":{"difficulty":4,"domain":1,"type":2,"isInternal":false},"tags":["javascript","frontend","promises","async programming","async await","js fundamentals","devtools tech","youtube","frontend masters","egghead","codedamn","youtube video","tutorial","blog"],"slug":"things-every-engineer-should-know-about-promises-in-javascript-or-frontend-fundamentals---rid---CpzShsPEajyOwTavUu5O","title":"Things Every Engineer Should Know About Promises in JavaScript | Frontend Fundamentals","resourceId":"CpzShsPEajyOwTavUu5O"},{"_id":"636cd77320622762d08cfe22","content":{"difficulty":1,"domain":2,"type":1,"isInternal":true},"tags":["javascript","frontend","code","array","interview questions","array polyfills","advanced javascript","js interview questions","hackerrank","codedamn","coding","interview preparation"],"slug":"top-10-javascript-interview-questions-based-on-array-polyfills---rid---oNzRUjONl4YB5ixIEGrl","title":"Top 10 JavaScript Interview Questions based on Array Polyfills","resourceId":"oNzRUjONl4YB5ixIEGrl"},{"_id":"5f1ff867cbec5f7ffc0c2fb6","content":{"difficulty":2,"domain":2,"type":1},"tags":["javascript","frontend","reactjs","profiling","performance","chrome","devtools","advanced frontend"],"slug":"profiling-react-performance-with-react-16-and-chrome-devtools---rid---Oj6vkw9ga5NIqw4266kr","title":"Profiling React performance with React 16 and Chrome Devtools.","resourceId":"Oj6vkw9ga5NIqw4266kr"}]}}