Webpack magic and AST traversing study.

Igor Davydkin
Apr 23, 2018 · 5 min read

Recently, I’ve added a new addon to the Storybook ecosystem, that will help documenting examples of the supported frameworks 😉 This addon utilizes a custom Webpack loader with a static code analysis and I find it interesting and worthwhile to share. I will explain a “behind the scenes” of creating the addon, but not “what should you do to make it work” — for usage follow the documentation.

Storysource demo

Storybook is a fast growing OSS project. It already supports 5 frameworks: React, React Native, Vue, Angular and the last (but not least) is Polymer. With all this growth, we faced a problem of a documenting new features.

We have a variety of examples for every framework, but all of these examples are located inside a big monorepo, and are hardly explorable by people. Besides, if one wants to run any of those examples, it requires cloning and bootstrapping the entire monorepo.

Needless to say that contributing to open source is mostly a volunteer effort - documenting every single example doesn’t scale for us, thus we need a help from community, or some kind of automation solution.


Storybook is already having a netlify build step. Every branch pushes a static version of all the examples to the netlify cloud.

deploy/netlify

Every example app is accessible as a sub-domain of a storybook account in netlify. For example this — https://release-3-3--storybooks-vue.netlify.com — is a link to the Vue example from the 3.3 release branch.

So, apparently we are able to point users to these static builds to look for the examples. But they will only see a fully functional application, without actually learning how to do stuff. Of Course, curious minds will be able to debug a transpiled and bundled code even without source maps, but we also want to help those capricious people that can’t live without good conditions.

Creating a Storybook addon that will show source code of the selected story in the addons pane — sounds like a dog-foodiest dog-fooding food ever 😈

High Level

In a high level, to achieve the said above we need to write the addon that will expose a decorator named withStorySource(source) that will be used in stories like this:

Example of the storysource usage

Just take a look what should be done to achieve this:

Flow for the hand use of the addon
  1. Create a story
  2. Get the string version of this story: it can be passed through the JSON.stringify() to get a well escaped string.
  3. Import addon-storysource to the initial story file and use it in the decorator with the stringified source from #2

Looks like a Sisyphean task adding this decorator to every story. Besides, it seems a very ugly solution if some need to copy paste the source as a string and put it to the decorator… Storybook app is already strongly tied with Wepback. So — Webpack to the rescue.

Webpack loader

Webpack loader is a preferred solutions for me in this issue. It’s hooked before each module is resolved. We only need to configure it to be the first in the chain in order to catch the initial state of the source — we don’t want to show the source file after transpiling 🤷.

The idea of our custom Webpack loader is to automate decoration of the stories with their source code. We don’t want to change every existing example by hand. Also, we don’t want to forget decorating future examples.

Webpack loader is just a function that takes source and returns manipulated source. For example, in babel-loader the incoming source could be javascript in ES7 and the output could be javascript in ES5. A basic structure looks like this:

function transform(source) {
return changeSource(source);
}

Our loader does the following (simplified version):

Custom loader

In short, it injects the addon-storysource decorator usage to every story and persists a variable with the initial source code (__STORY__) inside a changed code. This variable then, used to show the initial source in the addon pane.

AST Traversing

In order to inject a decorator we need to understand an abstract syntax tree (AST) of the story. AST is a preferred way when it comes to code parsing and analyses. Why not regex you ask ? Let me google it for you 😉.

For this purpose I’ve used the AST explorer in order to play nicely with the tree

AST Explorer with the basic story usage

From the image 👆 we can see that we need to find a “callee” which is named “storyOf”. Also we can see in the found AST node a position of this node in the source, and easily inject there a new addDecorator(…) statement.

For this purpose I’ve used:

  • acorn: to parse the source to AST.
  • estraverse: for easy travelling through the tree.

Formatting

As a follow up to this feature, I thought it will be nice to remove the “ugly” comments from the source. Nobody want’s to see eslint violations in the live code examples 😅. So to remove these comments, I’ve used an acorn’s feature that retrieves all the comments from the AST.

Then, we need to understand what comments are indeed “ugly” — not all comments are born the same:

Simplified version of the “ugly” comments extractions

After comments removal (And even before), the code might become not so pretty. So much luck we have a prettier to format our source =)

Formatting source with prettier

UI Part

Actually it was the easiest part. I’ve used a react-syntax-highlighter lib that does everything you want. It was as easy as just adding a simple react component that wraps a string 😙

Usage of the react-syntax-highlighter

The result is more than a satisfying (of course with the dark theme).

Result of using the react-syntax-highlighter in the addons panel

To make a long story short, if you want to use this addon in your Storybook setup, it will be available from the 3.4 version release. Also, I hope, some of the writing will be helpful for your work as well.

Few links to finish with

Storybook

The UI development environment you'll ♥️ to use.

Thanks to ɐqɐɹ iuɐɥs and Michael Shilman

Igor Davydkin

Written by

Storybook

Storybook

The UI development environment you'll ♥️ to use.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade