How to access build time assets during run time with Webpack
Tags: webpack, storybook, vue
Published: 2019-12-20 18:28:38

---
title: How to access build time assets during run time with Webpack
published: true
description: Making variables from assets that can be accessed globally
tags: webpack, storybook, vue
---

TL;DR;
How to retrieve information that is only available during build time when you are running your app.

I want to share a solution to a problem I had last week. I have hundreds of icons that can be used inside a component and these icons should be available inside [Storybook](https://storybook.js.org/). 

This is a perfect task to automate.

Everyone that has worked with Webpack, knows that you have global variables that can be used in runtime. Usually `isDevelopment` or `isProduction` will be used to check which environment the app is running and deliver the proper assets.

My solution was to retrieve all the icons' names during build time and save them inside a global scoped variable that would then be accessible during run time. For this example I used Vue, but this is something that Webpack would offer in any environment.

```javascript
// config/storybook/config.js

export const STORYBOOK_ICONS = require
    .context('../../src/assets/icons', true, /^.*\.svg$/) // Comment 1
    .keys()                                               // Comment 2
    .toString()                                           // Comment 3
    .match(/\b(?!svg)\b[^,./].+?(?=\.)/g)
```
*Comment 1*
We use here the [context API](https://webpack.js.org/guides/dependency-management/#context-module-api) from Webpack. This is a function that receives four parameters (I only used the first three):

```javascript
require.context(directory, useSubdirectories = true, regExp = /^\.\/.*$/, mode = 'sync');
```
*Comment 2*
Then we access all the keys with `.keys()`:

> keys is a function that returns an array of all possible requests that the context module can handle.

At the moment this is looking like this:

```javascript
STORYBOOK_ICONS = [
  "./about.svg",
  "./accept_database.svg",
  "./add_column.svg",
  "./add_database.svg",
  "./add_image.svg",
  "./add_row.svg",
  "./address_book.svg",
  "./advance.svg",
  "./advertising.svg",
  "./alarm_clock.svg",
  ...
]
```
Thats no good, we want to use the variables inside our component. We need to filter the name out. 

Regex to the rescue!

*Comment 3*
I preferred here to use the `.toString()` method instead of [.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) since I would need to create a new Regex variable and use that to compare with each passed value (as seen in [this](https://stackoverflow.com/questions/47008384/es6-filter-an-array-with-regex) SO answer). With a string we can then use the `.match` function that will return an array for every item that it finds with a certain regex.

Talk about killing two birds with one stone!

This creates an array without the file format.

```javascript
STORYBOOK_ICONS = [
    "about",
    "accept_database",
    "add_column",
    "add_database",
    "add_image",
    "add_row",
    "address_book",
    "advance",
    "advertising",
    "alarm_clock",
    ...
]
```

Then in the Story that I wanted to have the icons available. I import the variable from the global scope and using [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) and ES6 _Dynamic Property Keys_ to create a shiny new object with key and value pairs that are identical. 
Like this:

```javascript
// src/components/IconSelector/IconSelector.stories.js

import {STORYBOOK_ICONS} from '../../../config/storybook/config';

const iconList = STORYBOOK_ICONS.reduce(function (result, item) {
    result[item] = item;
    return result;
}, {});

```
Our new object looks like this:
```javascript
iconList = {
    "about": "about",
    "accept_database": "accept_database",
    "add_column": "add_column",
    "add_database": "add_database",
    "add_image": "add_image",
    "add_row": "add_row",
    "address_book": "address_book",
    "advance": "advance",
    "advertising": "advertising",
    "alarm_clock": "alarm_clock",
    ...
}
```

Which is then used inside the story:

```javascript
// src/components/IconSelector/IconSelector.stories.js

storiesOf("Icon Selector", module)
  .addDecorator(withKnobs)
  .add("default", () => {
    return {
      components: { IconSelector },
      props: {
        selectedIcon: {
          default: select(
            'Icons', iconList, Object.keys(iconList)[0]), // <- here
        },
      },
      template: `<IconSelector :icon-name='selectedIcon'/>`
    };
  });

```
And the final results:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/m05ypf11dh3wad1ikap6.gif)


#### Repo
I published a demo repo that can be found [here](https://github.com/aboutandre/storybook-webpack-global-icons).

#### PS
This is my first post ever. If you have any comments or pointers I would be glad to receive feedback! =)