Interchangeable configuration files in Webpack - javascript

I'm trying to bundle a react project that can run in multiple environments and needs different configurations to run on each one. I have it set up with browserify, but have recently been looking to move to webpack.
The idea goes like this: there's a js directory with a bunch of js files, of which main.js is the entry point. There is also a config folder with development.js, staging.js, production.js and so on. I am importing the configuration from all files using import config from './config'. So I want to be able to create a main.bundle.js file with all our code, and another config.js whose contents can be replaced with those of {environment}.js.
This allows us to choose the configuration for the app in deploy time, not at build time, just by copying over the contents of {environment.js}.
I have attempted to unsuccessfully use webpack's CommonsChunkPlugin, but it complains at bundle time that js/config.js is not there (obviously).

Why not make multiple builds, one with each config, and then deploy the one you want? There's different ways you could load the desired config for the build, but one very way would be to use the DefinePlugin to define constants for each environment, write a conditional against those constants to load the corresponding config, and let the Uglify plugin's dead code elimination pare down the conditional to the one valid case. I've used this approach in a number of places to handle per-environment conditionals without bloating the production code.

Related

How could I write a Webpack plugin that updates `resolve.modules` and `resolve.alias` in `--watch` mode at runtime?

I work in a large monorepo that has very many files that are processed by Webpack. One of our main patterns is that we use fully-qualified filenames for every module name and import. For example, if I have a file with path "/path/to/module/MyCoolModule.js", the import would simply look like import MyCoolModule from 'MyCoolModule' without needing any explicit path to the module. This is enabled by recursively scanning the project's directories for all relevant .js files/folders at Webpack init and then passing in the scanned files into resolve.modules and resolve.alias.
We like this pattern, and it works well for general usage when webpack is run in watch mode; however, it breaks down when new folders and files are added to the project while Webpack is already running. Because the files and folders are only scanned at Webpack init, Webpack is unable to resolve imports for new files/folders added since it started, so compilation will fail. This requires us to restart Webpack, which takes 1-2 minutes on our project. Across many engineers when switching between git branches or updating changes from master, this can add up to a significant slowdown in raw time and in breaking flow.
I'd like to write a plugin that will dynamically update the resolve.modules and resolve.alias such that we do not have to restart Webpack when new folders and files are added to the project. However, I am at a loss for how to do this. It seems like I have three main questions:
What hook(s) can I tap into to be notified when Webpack detects new files or folders?
Where would I put this plugin to access the above hooks? Would it exist in the top-level plugins config? resolve.plugins? etc.. As I'm understanding it, where the plugin is placed/configured will affect what hooks are available to it
How would I make Webpack aware of these dynamic updates? I am guessing there's a reference to the resolve obj somewhere that holds resolve.modules and resolve.aliases that Webpack actively references, but I'm not sure where that would live or where to access it (this could be completely wrong too)
(note: we are technically using Webpack 4, but answers for Webpack 5 are welcomed because I know we're behind there, and I'd guess a Webpack 5 solution would still point me in the right direction)
Thanks!

Fixing/adding module imports for entire workspace

I'm using vscode to build a larger TypeScript application. Let's say one day I decide to use absolute paths for module imports, because I want to be more directory structure agnostic. So I add the appropriate configuration, also configure vscode to prefer non-relative imports.
But I end up with lots of already existing imports in every file, which is a huge work to manually fix. What I can do file by file is delete all imports, and use the automatic imports for that file. Still, I have to do this for every piece of source file, which is still a nightmare.
Is anyone aware of any kinds of built-in or extension tooling which can help in situation like this? What I could image is something that resets all the imports in every file and re-imports automatically everything, based on the new settings.

Different babel or webpack configuration based on entry point

In my application, I am applying babel using webpack (which I use to generate my bundles from my source files). I create one bundle that runs on the server (entry point server_rendering.js) in an ExecJS environment and another that runs on the client (entry point application.js). Due to some peculiar interactions with certain JS libraries, I need to run different babel transformations dependent on the entry point file. How might I do this?
To clarify, there seem to be a number of similar questions concerned with babel responding differently based on environment variables which is not what I want to do here. I want to be able to compile both files as part of the same webpack process.
In my situation, I could also be satisfied with augmenting my webpack configuration based on the entry point.
You would need multiple Webpack configs, for example by returning an array of configs from webpack.config.js. Webpack can't do what you ask with one config because entrypoints are just that, entry points into one application. A given file will only be compiled a single time within an application, so there isn't any way to compile the same file multiple ways.
How you integrate that with Babel is up to you, but you could for instance pass envName: "client" to babel-loader in your client-side Webpack config, and envName: "server" to Babel your server Webpack config. Then your Babel config can select the set of plugins based on that value.

How to conditionally import different files during build in Angular 6?

Overview
I have Angular 6 app build by Angular CLI. Application is written in multiple languages. Each language has JSON file with translations of all the application texts. The filenames are ${language}.json, e.g. en.json, es.json, pl.json.
I'd like to set the language of the app during build - build the app only in one language. For that on build I need to import a single translation file, which is chosen depending on which language I'm building for.
Problem
How to conditionally import single translation file only for the language chosen during build?
In addition I'd like the translation file to be bundled together with the rest of my application's JavaScript, instead of being loaded on demand on run-time.
Sketch of a solution
const translations = import(`./translations/${lang}.json`);
Actually the above code works in Angular 6/Webpack (the only thing needed is setting compilerOptions.module: 'ESNext' in tsconfig.app.json), but not in a way that I want. Instead of resolving the import during build time and bundling the translation file together with the rest of the code, it creates a separate files (chunks) for all translations, one of which is then loaded on demand during run-time depending on the value of JavaScript lang variable.
So how do I make the import to be resolved during build-time and not run-time? Also where and how do I set lang variable during build, as I don't have access to Webpack configuration (it's hidden inside Angular CLI)
Webpack alone
When using Webpack alone, it's possible to implement this for example by using NormalModuleReplacementPlugin. But as Angular CLI hides all the Webpack configuration, I don't know how to integrate it into Angular project. Ejecting Webpack configuration from Angular CLI is the last resort for me.
As part of the CLI 6.1 release, a feature was added that allows files to be replaced during the build other than .ts files. Previously this was used for the environment and environment.prod files
Additional files can now be set with the fileReplacements key of the angular.json configurations. As far as I know, it should work with asset files, so .json should work. It's only been added recently, but there are issues/feature details that you can look up
Let us know if it works out!
P.S. If you don't want to complicate the angular.json configuration, you could create a .js node script, and run it before doing the build, passing in the language as a param, and replacing the relevant .json file in before building the Angular bundles

Brand wise building with Webpack

I have a requirement to do webpack build based on the brand (use brand specific css to build). I want to pass the brand as an agrument while building. for example I have 2 brands brandA and brandB, I will have build commands as below.
npm build --brandA
npm build --brandB
Is there a solution for this kind of brand wise build in webpack? Please help.
Yes, webpack is really good at this sort of thing once you get familiar with it. There are a few different ways you could approach it, but they all relate to how you set up your webpack config.
Write separate webpack config files for each 'brand.' This is the closest match to your example, because webpack has a CLI flag for specifying a config file to use, e.g. webpack --config webpack.config.brand-a.js
But most of your webpack config will probably be the same for each brand(especially if it's only the CSS that's changing). So rather than have duplicate code, you split out the parts that are the same in each brand's config file, maybe put each chunk in a /webpack directory at the root of your project as separate little JS modules. So you have a module for the loader config, and one for the plugin config, etc., and your config files each require/import those. Now your config files have a lot less boilerplate in them and are mostly just the parts that differ from build to build.
Once you start splitting up your webpack config there's a lot of ways to organize it. Although the normal webpack config format is to export a single config object, it also allows you to export an array of config objects from your config file -- in this case webpack will do a separate build for each config object in the exported array. So another way to implement this would be a single webpack.config.js file, where you write a little code to iterate through the brands and, for each one, push a config object into an array -- then export the whole array. The upside here would be you could run all the builds with a single command, as webpack takes care of creating separate processes for each build.
A word of warning -- build time can get pretty hefty. (Although I haven't been deep into it since v1.x and things have probably gotten better.) If you build a lot you might find that it's worth looking into webpack optimization to speed up the process.

Categories