✏ Handle CSS in webpack | Inline CSS
Tags: webpack, css, javascript, beginners
Published: 2020-08-04 19:17:04

These post is the first of a trilogy. *This is where it all began...* 🧙

| 🔎 Focus on | 🖌 CSS Handling | Parts |
|:-:|:-:|:-:|
| (*Index*) | | [#️⃣](https://dev.to/didof/handle-css-in-webpack-index-34kj) |
| **development only** | **inline CSS** | 📌 |
| *both dev & prod* | *mini-css-extract-plugin* |[2️⃣](https://dev.to/didof/handle-css-in-webpack-extract-css-3i4g)|
| *production only* | *CSS modules* | 3️⃣ |

### Example Code 📜
> [webpack-inline-css](https://github.com/didof/webpack-inline-css)

---

# Final Product 🤖
At the end of the first part you will have built a flexible scaffolding for your webpack configuration. Hard work will be rewarded in later parts.
The first-stage final product will run only in development and will be able to inject CSS directly in the bundle.

---

# Flow of Thought 🏮

1. Install *packages*
2. Add *start* script
3. Add *JavaScript* and *CSS* file
4. Instruct *webpack*
 1. Read **env**
 2. Custom *loader*
 3. Inject *rules* + *interface*

---

## Implementation 🤓

### - 1 - Install *packages*
Donwload the webpack triad, the two **loaders** and the only needed **plugin**. In the terminal invoke `npm i -D webpack webpack-cli webpack-dev-server css-loader style-loader html-webpack-plugin`.

### - 2 - Add *start* script
If not present, quickly create a `package.json` by calling `npm init -y`. Add a script to activate the server in development mode:

##### package.json
```json
{
 ...
 "scripts": {
    "start": "webpack-dev-server --env development"
  },
 ...
}
```

### - 3 - Add *JavaScript* and *CSS* file
Create a `src` folder. In there add a `style.css` and give it some instruction:

##### style.css
```css
body {
 background-color: indigo;
 color: white;
 padding: 30px;
}
```

In the `src` folder place a `index.js`. By default, webpack is educated to look for an entry at this path (*/src/index.js*).
For the purpose of this demonstration it is sufficient to add simply the import of the *stylesheet*:

##### index.js
```js
import './style.css';
```

> Of course, in a real project it is advisable to use a nested architecture.

### - 4 - Instruct *webpack*
If you try to give the command `npm start` you will see that the server actually starts, finds `index.js` and reads it. but when it’s time to read the css it starts to complain - webpack still does not know the css language.
We can fix that creating in the root a configuration file named `webpack.config.js`.
The hardcoded solution is pretty linear:
##### webpack.config.js
```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
devServer: {
		open: true,
		stats: 'errors-only',
	},
	module: {
		rules: [
			{
				test: /\.css$/i,
				use: ['style-loader', 'css-loader'],
			},
		],
	},
	plugins: [
		new HtmlWebpackPlugin({
			title: 'Webpack Inline CSS',
		}),
	],
};
```

In the terminal call `npm start` - the result is there, your favorite color is smeared on the newly opened tab.
> However, this type of approach is not flexible and after a few additions we will start to suffer confusion.

I want to build my personal **webpack-**🤖 with these features: 
* able to integrate various parts
 * each with a clear interface
* double behavior based on environment variable
* (perhaps also polite and clean)

### - 4|a - Instruct *webpack* | Read **env**
First export a function that returns a configuration object:
##### webpack.config.js
```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const useRules = require('./config/webpack.useRules');

module.exports = (env) => ({
	devServer: {
		open: true,
		stats: 'errors-only',
	},
	plugins: [
		new HtmlWebpackPlugin({
			title: 'Webpack Inline CSS',
		}),
	],
	module: useRules(env),
});
```
**useRules** it does not exist yet. However we can note that it requires a single argument, **env**. We will instruct *useRules* to return different rules (then different webpack behaviors) for different environments.

### - 4|b - Instruct *webpack* | Custom *loader*
In the root create a `config` folder. In it place `loaders.js`. Here we can store all loaders that could be needed in the future. The following piece of code exposes the function `loadCSS` that may or may not get a `config` argument.

##### loaders.js
```js
function addConfigs(r, c) {
		Object.entries(c).forEach(([key, value]) => {
			if (!value) return;
			r[key] = value;
		});
		return r
	}

exports.loadCSS = (config = {}) => {
	// basic rule
	const rule = {
		test: /\.css$/i,
		use: ['style-loader', 'css-loader'],
	};

	return addConfigs(rule, config);
};
```
The reference code below rappresent the output. Not passing any configuration object, it would lack of the exclude property (that is indeed the default output).
##### loadCss({ exclude: ['node_modules'] })
```js
{
   test: /\.css$/i,
   use: ['style-loader', 'css-loader'],
   exclude: ['node_modules']
};
```

> Thanks to `style-loader`, the previously CSS parsed by `css-loader` is injected directly in the bundle. That's okay when developing but we'll need another solution for production. This file will contain the production-funtion (addressed in the second chapter).

### - 4|c - Instruct *webpack* | Inject *rules* + *interface*
Lastly we need an adapter between the newly created function and the core webpack configuration file. Why not take advantage of it and build one that in addition to returning an informative message, allow in the future a simpler implementation of new loaders?

In the `config` folder create `useRules.js`. At first, the following code could seem overwhelming, but we are going through it togheter.
##### useRules.js
```js
const { loadCSS } = require('./loaders'); //[1]

module.exports = (env) => { //[2]
	const loaders = { //[3]
		css: (i) => {
			switch (i) {
				case 'inline':
					return loadCSS()
				default:
					throw new Error('production is not implemented yet');
			}
		},
	};

	// developer interface [4]
	const instructions = { 
		css: {
			development: 'inline',
			production: 'extract',
		},
	};

	// business logic [5]
	let message = '[useRules] ';
	const rules = Object.entries(instructions).map(([key, value]) => {
		const i = instructions[key][env];
		message += key + '|' + i;
		return loaders[key](i);
	});

	console.info(message);
	return { rules };
};
```

1. Import the formerly made loader.
2. Export a function that gets **env**
3. Create a container of functions. Add one inside that will manage the CSS. It gets an instruction (*i*) as 'inline', 'extract' or 'modules' and activates the appropriate *loader*. At this point we only have one loader - it will be used when *i* is equal to 'inline'.
4. Since we don't want to get hands dirty every time a loader need to be added, we are building a *developer interface* - it's just a set of informations (once again, only for CSS right now) that the developer can easily change and otherwise quickly get a different behavior.
5. The last piece is iterate for each instruction (only one right now) and extract the **env**-associated command.

---

# Checking the Outcome 😎
> When using `npm start`, **env** equals development. So *i* is 'inline'. This causes the activation of the loader that, not by chance, injects the CSS in the bundle.

> Let's suppose we already had built another loader and we had associated it with its case in the switch - simply changing the command in the developer interface [4] we could observe the different behavior.

> Peek in the console - you should see `[useRules] css|inline`.

---

## Upgrade the **second-stage** 🔝

> [Let's go 🐉](https://dev.to/didof/handle-css-in-webpack-extract-css-3i4g)