<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thomas Allmer</title>
    <description>The latest articles on DEV Community by Thomas Allmer (@dakmor).</description>
    <link>https://dev.to/dakmor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F102337%2Fe5056daf-4302-42c2-905e-ed563b17d404.jpeg</url>
      <title>DEV Community: Thomas Allmer</title>
      <link>https://dev.to/dakmor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dakmor"/>
    <language>en</language>
    <item>
      <title>Introducing check html links - no more bad links</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Wed, 27 Jan 2021 11:59:33 +0000</pubDate>
      <link>https://dev.to/modern-web/introducing-check-html-links-no-more-bad-links-1jdg</link>
      <guid>https://dev.to/modern-web/introducing-check-html-links-no-more-bad-links-1jdg</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR : I created a standalone tool that can help you fix all the broken links in your websites/documentation. You can check it out &lt;a href="https://www.npmjs.com/package/check-html-links"&gt;on npm as check-html-links&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my developer career, I have put live multiple websites and honestly often within a few days, there was always this one issue raised. "This link on xxx is broken". 🤦‍♂️&lt;/p&gt;

&lt;p&gt;Often these things happen as somewhere a page got moved or renamed and not every location got updated.&lt;br&gt;
It's really hard to catch especially if you have a dynamic page like with WordPress or an SPA. And for users, there is nothing worse than landing on your documentation only to find a 404 staring back at them.&lt;/p&gt;

&lt;p&gt;Luckily, with the rise of SSG (Static Site Generators), this problem becomes easier to tackle and can be solved in large part. The reason for that is that with all HTML rendered upfront as static files we can read all of them and check every link.&lt;/p&gt;
&lt;h2&gt;
  
  
  Evaluation and the decision for a new tool
&lt;/h2&gt;

&lt;p&gt;Of course, I am not the first one to come up with that idea and there are multiple tools available on the market already.&lt;br&gt;
However, when checking existing solutions I found out that most of them didn't satisfy me in at least on way 😅. Things I noticed: slow to execute, deprecated, large dependency tree, confusing output for the user, ...&lt;/p&gt;

&lt;p&gt;Reviewing these tools I decided to create my own, with the following requirements :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blazing fast&lt;/li&gt;
&lt;li&gt;User-focused output&lt;/li&gt;
&lt;li&gt;Few dependencies, to keep it lean&lt;/li&gt;
&lt;li&gt;Preferably in the NodeJS ecosystem&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Focusing on Useful Output
&lt;/h2&gt;

&lt;p&gt;Most tools evaluated check files individually and report on their findings individually. That means if you have a broken link in your header or footer, you will get one line (or even multiple lines) of an error message(s) for EVERY page.&lt;/p&gt;

&lt;p&gt;I tested this on the &lt;a href="https://github.com/11ty/11ty-website"&gt;11ty-website&lt;/a&gt; and there are currently 516 broken links in 501 files. However, &lt;strong&gt;the source of those 516 broken links is just 13 missing pages/resources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In my implementation, I decided to switch from an "Error in File Focused" method to a "Missing File Focused". Let's see this with examples&lt;/p&gt;
&lt;h3&gt;
  
  
  Error in File Focused
&lt;/h3&gt;

&lt;p&gt;This is what a lot of current existing solutions implement. Here is part of the output that is being produced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...]
authors/ryzokuken/index.html
  target does not exist --- authors/ryzokuken/index.html --&amp;gt; /speedlify/
authors/alex_kaul/index.html
  target does not exist --- authors/alex_kaul/index.html --&amp;gt; /speedlify/
docs/config/index.html
  target does not exist --- docs/config/index.html --&amp;gt; /speedlify/
  hash does not exist --- docs/config/index.html --&amp;gt; /docs/copy/#disabling-passthrough-file-copy
authors/cramforce/index.html
  target does not exist --- authors/cramforce/index.html --&amp;gt; /speedlify/
authors/accudio/index.html
  target does not exist --- authors/accudio/index.html --&amp;gt; /speedlify/
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get ~2000 lines of errors for &lt;code&gt;/speedlify/&lt;/code&gt; as it's not found ~500 times. In the middle of those errors, we also see some other broken links.&lt;br&gt;
Because the reporting is focusing first on the files, and then on the actual error &lt;strong&gt;it is difficult to know where most errors originate from&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Missing File Focused
&lt;/h3&gt;

&lt;p&gt;Let us turn that around and focus on missing references indeed. Here is the output for the same input website :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...]
  1. missing reference target _site/speedlify/index.html
    from _site/404.html:1942:13 via href="/speedlify/"
    from _site/authors/_amorgunov/index.html:2031:13 via href="/speedlify/"
    from _site/authors/_coolcut/index.html:2031:13 via href="/speedlify/"
    ... 495 more references to this target

  2. missing id="disabling-passthrough-file-copy" in _site/docs/copy/index.html
    from _site/docs/config/index.html:2527:267 via href="/docs/copy/#disabling-passthrough-file-copy"

  3. missing reference target _site/authors/dkruythoff/github.com/dkruythoff/darius-codes
    from _site/authors/dkruythoff/index.html:2102:234 via href="github.com/dkruythoff/darius-codes"
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get one 5 line error for &lt;code&gt;/speedlify/&lt;/code&gt; and it tells us it's missing 495 times + 3 examples usages.&lt;br&gt;
Afterward, we find very clearly more missing references and where they occurred.&lt;/p&gt;
&lt;h3&gt;
  
  
  A clear winner
&lt;/h3&gt;

&lt;p&gt;Comparing those two outputs makes it pretty clear to me that &lt;code&gt;Missing File Focused&lt;/code&gt; will make more sense if there is a chance that some links will be broken everywhere. My implementation focuses on missing links in its output. This is crucial because it allows developers to know where to focus their efforts first to get the biggest wins.&lt;/p&gt;
&lt;h2&gt;
  
  
  Focusing on Speed
&lt;/h2&gt;

&lt;p&gt;Speed is always nice to have but in this case, it's probably vital. I need this to be fast so that I can run it potentially on every save. Speed is also very important in case the tool runs in a CI for example. For projects with extensive documentation, we don't want to hog the CI only to check for documentation.&lt;/p&gt;

&lt;p&gt;Luckily HTML is an awesome language to analyze as it's declarative, which means you can read and analyze it at the same time. This may even mean that the HTML is already processed by the time the file is done reading.&lt;/p&gt;

&lt;p&gt;With this knowledge I was hopeful - but reality didn't deliver 😅. The only tool that could keep up with the speed I needed was implemented in &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It seems that most tools use sophisticated parsers meant to create full syntax trees of your HTML.&lt;br&gt;
In reality for link checking all you need to know are the &lt;em&gt;id&lt;/em&gt; and the &lt;em&gt;href&lt;/em&gt; attributes.&lt;/p&gt;

&lt;p&gt;I have been using &lt;a href="https://github.com/justinwilaby/sax-wasm"&gt;sax-wasm&lt;/a&gt; in a few situations before and I knew it supported streaming. I knew that way it could be FAST 🤞!&lt;/p&gt;

&lt;p&gt;How fast are we talking about though?&lt;/p&gt;

&lt;p&gt;As a rule of thumb, I decided that the analysis should be finished within 1s for a small site (up to 200 pages).&lt;br&gt;
The main reason is already listed above: To not disturb during writing/development as it will run on every save.&lt;br&gt;
For medium sites (200 - 1000 pages), it's reasonable if it takes a little longer - let's aim for less than 5 seconds. This will probably be a breaking point where you execute it only on-demand and in the CI instead of executing it on every save.&lt;/p&gt;

&lt;p&gt;Results are gatherd on January 26, 2021:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Website&lt;/th&gt;
&lt;th&gt;Pages&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;open-wc.org&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;td&gt;~0.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11ty.dev&lt;/td&gt;
&lt;td&gt;501&lt;/td&gt;
&lt;td&gt;~2.5s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;web.dev&lt;/td&gt;
&lt;td&gt;830&lt;/td&gt;
&lt;td&gt;~3.7s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eslint.org&lt;/td&gt;
&lt;td&gt;3475&lt;/td&gt;
&lt;td&gt;~12.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Being part of the NodeJS ecosystem
&lt;/h2&gt;

&lt;p&gt;My daily workflow is hugely dominated by JavaScript, so it was only natural to want to stay in the same environment if I could reach my earlier requirements with it.&lt;br&gt;
On top of this, the end goal is to integrate it within a bigger WIP system called &lt;a href="https://github.com/modernweb-dev/rocket"&gt;Rocket&lt;/a&gt; which is node-based so therefore it will need to at least support NodeJS. Having it standalone (usable via &lt;code&gt;npx&lt;/code&gt;) also makes it more versatile and easier to maintain/test.&lt;/p&gt;
&lt;h2&gt;
  
  
  Focusing on a small Dependency Tree
&lt;/h2&gt;

&lt;p&gt;The JavaScript and NodeJs ecosystem is very active and constantly shifting. Lots of changes/improvements happen all the time. It's often hard to keep up. Therefore having a small dependency tree is something to always thrive for because it will reduce the maintenance burden down the line. And as an added benefit, it makes it smaller and easily embeddable as less stuff has to go down the wire. Lean is king 👑.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;As already mentioned I went on and implement a link checker myself 😅. So far it fits all my requirements so I call it a success 🎉! You can find it &lt;a href="https://www.npmjs.com/package/check-html-links"&gt;on npm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I called it &lt;code&gt;check-html-links&lt;/code&gt; and its slogan is "no more broken links or assets".&lt;/p&gt;

&lt;p&gt;The features so far are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extracts every attribute value of id, href, src, srset&lt;/li&gt;
&lt;li&gt;use a wasm parser (sax-wasm)&lt;/li&gt;
&lt;li&gt;streams the html for performance&lt;/li&gt;
&lt;li&gt;check if file or id within file exist&lt;/li&gt;
&lt;li&gt;focus on missing references/sources&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;It does check your final html output so you need to execute it after your Static Site Generator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx check-html-links _site
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage Github Action
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/jlengrand"&gt;Julien&lt;/a&gt; created a Github action available for the tool, so you can easily plug it in your existing CI. You can find it &lt;a href="https://github.com/marketplace/actions/check-html-links-action"&gt;on the GitHub Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a complete example workflow that will check the result of the folder &lt;code&gt;_site&lt;/code&gt; in the root of your repository on each push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;check_html_links_job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A job to test check-html-links-action&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-html-links-action step&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-links&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;modernweb-dev/check-html-links-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;doc-folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_site_'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;p&gt;Checking the output of &lt;a href="https://github.com/11ty/11ty-website"&gt;11ty-website&lt;/a&gt; with 13 missing reference targets (used by 516 links) while checking 501 files. (on January 17, 2021)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Lines printed&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Lang&lt;/th&gt;
&lt;th&gt;Dependency Tree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;check-html-links&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;~2.5s&lt;/td&gt;
&lt;td&gt;node&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;link-checker&lt;/td&gt;
&lt;td&gt;3000+&lt;/td&gt;
&lt;td&gt;~11s&lt;/td&gt;
&lt;td&gt;node&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hyperlink&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;td&gt;4m 20s&lt;/td&gt;
&lt;td&gt;node&lt;/td&gt;
&lt;td&gt;481&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;htmltest&lt;/td&gt;
&lt;td&gt;1000+&lt;/td&gt;
&lt;td&gt;~0.7s&lt;/td&gt;
&lt;td&gt;GO&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;The basic functionality is finished and it's reasonabley fast.&lt;/p&gt;

&lt;p&gt;Topic to work on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow to ignore folders (potentially via a cli parameter)&lt;/li&gt;
&lt;li&gt;Support for &lt;code&gt;&amp;lt;base href="/"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Big Sites Speed improvements (potentially running multiple parsers in parallel for 1000+ pages)&lt;/li&gt;
&lt;li&gt;Speed improvements by introducing a "permanent cache" for the parse result (if file did not change, parse result will not change - we still check all links)&lt;/li&gt;
&lt;li&gt;Memory consumption check (see if there is room for improvements)&lt;/li&gt;
&lt;li&gt;Improve node api&lt;/li&gt;
&lt;li&gt;Check external links&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;Thank you for following along on my journey on creating &lt;code&gt;check-html-links&lt;/code&gt;. You can find the code on &lt;a href="https://github.com/modernweb-dev/rocket/tree/main/packages/check-html-links"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/modern_web_dev"&gt;Twitter&lt;/a&gt;, or follow me on my personal &lt;a href="https://twitter.com/dakmor"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://twitter.com/jlengrand"&gt;Julien&lt;/a&gt; for feedback and helping turn my scribbles to a followable story.&lt;/p&gt;

&lt;p&gt;If you think my open source work is valuable then I would like you to check out my personal &lt;a href="https://github.com/sponsors/daKmoR"&gt;Github Sponsor Page&lt;/a&gt;. Or you can support our whole group via the &lt;a href="https://opencollective.com/modern-web"&gt;Modern Web Open Collective&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@mihaiteslariu0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Teslariu Mihai&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>node</category>
    </item>
    <item>
      <title>Introducing mdjs - interactive demos everywhere</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Wed, 15 Apr 2020 12:26:09 +0000</pubDate>
      <link>https://dev.to/open-wc/introducing-mdjs-interactive-demos-everywhere-16dh</link>
      <guid>https://dev.to/open-wc/introducing-mdjs-interactive-demos-everywhere-16dh</guid>
      <description>&lt;p&gt;All shared code should have written documentation to show what it can be used for and what the idea behind it is.&lt;br&gt;
Users should at least be able to get a high level understanding of what they are using, what they're using it for, and why.&lt;/p&gt;

&lt;p&gt;On the web, we have many many different ways of writing documentation.&lt;br&gt;
However, one thing almost all of them have in common is that they rely on &lt;a href="https://en.wikipedia.org/wiki/Markdown" rel="noopener noreferrer"&gt;Markdown&lt;/a&gt; or some kind of variation of it.&lt;/p&gt;

&lt;p&gt;And it's no surprise, because Markdown is supported practically everywhere (vscode, atom, github, gitlab, dev.to, npmjs, ...)&lt;/p&gt;
&lt;h2&gt;
  
  
  For tools that do not run in the browser
&lt;/h2&gt;

&lt;p&gt;In this case, you will mostly share code snippets that people will need to run in their own projects in which case traditional static site generators like &lt;a href="https://docusaurus.io/" rel="noopener noreferrer"&gt;Docusaurus&lt;/a&gt;, &lt;a href="https://vuepress.vuejs.org/" rel="noopener noreferrer"&gt;VuePress&lt;/a&gt;, &lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt;, et al work great. All of them have full support for Markdown and allow you to easily create beautiful documentation pages with code snippets/highlighting, and more.&lt;/p&gt;

&lt;p&gt;And frankly, if that is your use case almost everything you need should be possible with those tools as long as you feel comfortable with the ecosystem/framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  For (visual) components that do run in the browser
&lt;/h2&gt;

&lt;p&gt;In this context, users probably do expect a live demo to see all the different options of your component in action. So pure Markdown is usually not enough as we now want to actually execute code and "insert" our working component in our documentation somehow. This would require specialized handling for each framework.&lt;/p&gt;
&lt;h3&gt;
  
  
  Vue
&lt;/h3&gt;

&lt;p&gt;For Vue, as an example, you can use VuePress which auto registers all Vue components in a certain folder and then you can use as normal html tags since Markdown supports html&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└─ .vuepress
  └─ components
      ├─ demo-1.vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;demo-1&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;supports vue components and has "magical" import for them&lt;/li&gt;
&lt;li&gt;no support for generic javascript or passing properties to components&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  React
&lt;/h3&gt;

&lt;p&gt;For React you can use &lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;Mdx&lt;/a&gt; which extends Markdown with JSX support. Mdx is available via multiple tools like &lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt;, &lt;a href="https://www.docz.site/" rel="noopener noreferrer"&gt;docz&lt;/a&gt;, &lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;storybook&lt;/a&gt;, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;import { Chart } from '../components/chart'

&lt;span class="gh"&gt;# Here’s a chart&lt;/span&gt;

The chart is rendered inside our MDX document.
&lt;span class="nt"&gt;&amp;lt;Chart&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;supports import/export JavaScript&lt;/li&gt;
&lt;li&gt;passes everything through JSX&lt;/li&gt;
&lt;li&gt;Doesn't look &lt;em&gt;great&lt;/em&gt; on github, requires special tools in editors to get highlighting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;What all these specialized tools have in common is that they require a specific build tooling setup to work.&lt;br&gt;
For web components, none of that is actually needed. Markdown already allows for HTML. The only missing piece is how to load a web component through JavaScript?&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing Markdown with JavaScript (mdjs)
&lt;/h2&gt;

&lt;p&gt;The primary goals are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;minimal complexity&lt;/li&gt;
&lt;li&gt;follows progressive enhancement&lt;/li&gt;
&lt;li&gt;stick close to valid markdown syntax&lt;/li&gt;
&lt;li&gt;code highlighting in editors without additional tools&lt;/li&gt;
&lt;li&gt;looks good on github/gitlab/any source code management tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fundamental idea seems almost too simple to be true. We "enhance" a code fence block with additional meta data &lt;code&gt;js script&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```js script
import './my-component.js';
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This is my component
&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that's it! 😄&lt;/p&gt;

&lt;p&gt;Alright, enough talk, you can see it live here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://webcomponents.dev/edit/aPQdZ4FtAiqJ7YXnRe2s?pm=1&amp;amp;sv=1" rel="noopener noreferrer"&gt;==&amp;gt; Link to editable demo &amp;lt;==&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does it work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mdjs hooks into &lt;a href="https://remark.js.org/" rel="noopener noreferrer"&gt;remark&lt;/a&gt; and extracts all those tagged js blocks.&lt;br&gt;
In the end, html and js is separately available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;This is my component&amp;lt;/h1&amp;gt;&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;jsCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;import './my-component.js';&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can then be combined/processed by any tool to create an actual documentation page.&lt;/p&gt;

&lt;p&gt;The process looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract &lt;code&gt;js script&lt;/code&gt; and separate it from md&lt;/li&gt;
&lt;li&gt;Render md&lt;/li&gt;
&lt;li&gt;Provide html &amp;amp; js&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fmdjs-script-transform-resized.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fmdjs-script-transform-resized.gif" alt="mdjs script transform"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Link to &lt;a href="https://slides.com/dakmor/deck-mdjs#/" rel="noopener noreferrer"&gt;animation as slides&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This already is powerful enough to directly include JavaScript and render web components with attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing mdjs with demo format
&lt;/h2&gt;

&lt;p&gt;Now that we can execute JavaScript within our Markdown this opens the door for more advanced features.&lt;/p&gt;

&lt;p&gt;Our first step is to create another enhanced js code block, namely; &lt;code&gt;js story&lt;/code&gt;.&lt;br&gt;
From this code block you can export a function to be executed on demand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```js script
import './my-component.js';
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This is my component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```js preview-story
export const demo = () =&amp;gt; `&amp;lt;my-component header="from attribute"&amp;gt;&amp;lt;/my-component&amp;gt;`
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;if you want to add a border around and a button to show/hide the actual source code you can use &lt;code&gt;js preview-story&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What you get looks something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;This is my component&amp;lt;/h1&amp;gt;&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;jsCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;import './my-component.js';&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;export const demo = () =&amp;gt; `&amp;lt;my-component header="from attribute"&amp;gt;&amp;lt;/my-component&amp;gt;`&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, this adds an extra step to the processing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract &lt;code&gt;js script&lt;/code&gt; and separate from md&lt;/li&gt;
&lt;li&gt;Extract &lt;code&gt;js story&lt;/code&gt; and &lt;code&gt;js preview-story&lt;/code&gt; and separate from md&lt;/li&gt;
&lt;li&gt;Put a placeholder &lt;code&gt;&amp;lt;mdjs-story mdjs-story-name="demo"&amp;gt;&amp;lt;/mdjs-story&amp;gt;&lt;/code&gt; or &lt;code&gt;mdjs-preview&lt;/code&gt; at it's place&lt;/li&gt;
&lt;li&gt;Render markdown&lt;/li&gt;
&lt;li&gt;Provide html, javascript, and stories&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is all the information we need to create full javascript and demo capable pages purely from markdown.&lt;/p&gt;

&lt;p&gt;By default Mdjs takes it a small step further by supporting an actual template system - namely &lt;a href="https://lit-html.polymer-project.org/" rel="noopener noreferrer"&gt;lit-html&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```js script
import './demo-wc-card.js';
import { html } from 'lit-html';
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This is my component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```js story
export const demo = () =&amp;gt; html`
  &amp;lt;demo-wc-card header="HEADER"&amp;gt;&amp;lt;/demo-wc-card&amp;gt;
`;
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fmdjs-story-transform-dev-to-size.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fmdjs-story-transform-dev-to-size.gif" alt="mdjs story transform"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Link to &lt;a href="https://slides.com/dakmor/deck-mdjs#/" rel="noopener noreferrer"&gt;animation as slides&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here another playground mimicking a full documentation page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://webcomponents.dev/edit/PqrQkg3abvFJ7vxyZuqa?pm=1&amp;amp;sv=1" rel="noopener noreferrer"&gt;==&amp;gt; Link to editable demo &amp;lt;==&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  mdjs default docs page
&lt;/h2&gt;

&lt;p&gt;Once all this meta-information is available you can render a specific docs page.&lt;/p&gt;

&lt;p&gt;It basically comes down to generating this code which assigns the demo function to the actual web component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;story&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;demo&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storyEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rootNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[mdjs-story-name="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;storyEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;storyEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this happens under the hood for you 🤗&lt;/p&gt;

&lt;h2&gt;
  
  
  Where can you use mdjs?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  You can use it locally via es-dev-server
&lt;/h3&gt;

&lt;p&gt;Here i'll show you how you can create a github like markdown view for all your local markdown files including live demos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fes-dev-server-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fes-dev-server-screenshot.png" alt="es-dev-server screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install es-dev-server as a dependency by running &lt;code&gt;npm i -D es-dev-server&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the following script to your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es-dev-server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;es-dev-server.config.js&lt;/code&gt; in the root of your repo.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mdjsTransformer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mdjs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;nodeResolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;README.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;responseTransformers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mdjsTransformer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After executing &lt;code&gt;npm run start&lt;/code&gt; you can happily browse your live documentation via &lt;a href="http://localhost:8000/README.md" rel="noopener noreferrer"&gt;http://localhost:8000/README.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can see an example setup in the &lt;a href="https://github.com/daKmoR/demo-wc-card" rel="noopener noreferrer"&gt;demo-wc-card repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  You can use it via Storybook
&lt;/h3&gt;

&lt;p&gt;If you want to work on individual components or get a list of all demos you can use Storybook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fstorybook-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fstorybook-screenshot.png" alt="storybook screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install dependency &lt;code&gt;npm i -D @open-wc/demoing-storybook&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add to your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"storybook"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"start-storybook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Adjust your &lt;code&gt;.storybook/main.js&lt;/code&gt; to load markdown files
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../README.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../docs/**/*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;esDevServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;nodeResolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add to every markdown file that should be in storybook a name via
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Group/My Awesome Component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that, you are good to go.&lt;br&gt;
No additional changes to any files are needed; a plugin will take care of everything by converting your markdown files to the support Storybook's mdx format.&lt;/p&gt;

&lt;p&gt;For more detailed information please see &lt;a href="https://open-wc.org/demoing-storybook/" rel="noopener noreferrer"&gt;https://open-wc.org/demoing-storybook/&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Show it on github
&lt;/h3&gt;

&lt;p&gt;Since Github supports markdown out of the box, we can take things even further by using Mdjs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fgithub-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fgithub-screenshot.png" alt="github screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As it's not supported by github directly you will need a chrome extension called &lt;a href="https://chrome.google.com/webstore/detail/mdjs-viewer/ifkkmomkjknligelmlcnakclabgohafe" rel="noopener noreferrer"&gt;mdjs-viewer&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you want to see a demo without opening a different page? mdjs-viewer!&lt;/li&gt;
&lt;li&gt;Do you want to show a live example of the issue you are having? mdjs-viewer!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/gfecZUYtxv4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Almost looks like black magic, huh?&lt;br&gt;
All you did was install a Chrome extension and suddenly Github got superpowers.&lt;/p&gt;

&lt;p&gt;All that you need is to have some Markdown files with the correct code fence blocks, and have your code up and running on &lt;a href="https://unpkg.com" rel="noopener noreferrer"&gt;unpkg.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does it actually work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The extension detects which Github page you are on.&lt;br&gt;
If it actually finds a markdown file or an issue with mdjs code then it adds a "show demo" button to activate it.&lt;br&gt;
Only if you click the button it will start gathering all the needed info.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;find the nearest &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;read the actual markdown file/issue content&lt;/li&gt;
&lt;li&gt;replace all bare import with &lt;code&gt;unpkg.com&lt;/code&gt; imports&lt;/li&gt;
&lt;li&gt;replace all relative imports with &lt;code&gt;unpkg.com&lt;/code&gt; and the name of the package.json + relative path for it&lt;/li&gt;
&lt;li&gt;create a secured iframe&lt;/li&gt;
&lt;li&gt;position the iframe absolute as an overlays&lt;/li&gt;
&lt;li&gt;put the javascript and html code inside the iframe&lt;/li&gt;
&lt;li&gt;the button becomes a toggle to show/hide the iframe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the tasks are more complicated and require some extra work to make it secure but in essence, that's it.&lt;/p&gt;

&lt;p&gt;With that, you can put documentation with live examples on github.&lt;br&gt;
Even issues with demos showing the actual error in the code are possible.&lt;/p&gt;

&lt;p&gt;That sure sounds like a hell of a tool to improve your documentation an issue reproduction, doesn't it?&lt;br&gt;
Especially as the readme and issue content still remain useful even without the extension.&lt;/p&gt;

&lt;p&gt;For more detailed information please see &lt;a href="https://github.com/open-wc/mdjs-viewer" rel="noopener noreferrer"&gt;https://github.com/open-wc/mdjs-viewer&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supported on webcomponents.dev
&lt;/h3&gt;

&lt;p&gt;Fully supported by this awesome online editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fwebcomponents-dev-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fwebcomponents-dev-screenshot.png" alt="webcomponent.dev screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can directly edit your documentation, demos and code directly in the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fwebcomponents-dev-new.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fopen-wc%2Fblog-posts%2Fmaster%2F2020-04-introducing-mdjs-interactive-demos-everywhere%2Fimages%2Fwebcomponents-dev-new.png" alt="webcomponent.dev screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can start directly with documentation as in the screenshot above, or even better you can use it in every Markdown file or README.md 💪&lt;/p&gt;

&lt;p&gt;Give it a go and document your components in all its glory.&lt;/p&gt;

&lt;p&gt;All the demo links are actually from &lt;a href="https://webcomponents.dev/edit/collection/lsZ2eaviDNwy6pIBEDeL/tS7JYfymt6yeshma8Gn1?pm=1&amp;amp;sv=1" rel="noopener noreferrer"&gt;webcomponents.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Be sure to &lt;a href="https://webcomponents.dev/" rel="noopener noreferrer"&gt;check it out&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How you can add support for mdjs
&lt;/h2&gt;

&lt;p&gt;Please check the official documentation page at &lt;a href="https://rocket.modern-web.dev/docs/markdown-javascript/overview/" rel="noopener noreferrer"&gt;https://rocket.modern-web.dev/docs/markdown-javascript/overview/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resume
&lt;/h2&gt;

&lt;p&gt;There you have it - mdjs is a format that can be shown in many different ways.&lt;br&gt;
It is your single source of truth for good looking documentation everywhere.&lt;br&gt;
Be it locally, a published storybook, on github or npmjs it always looks good even if there is no direct support for it, but when possible it will become interactive demos through progressive enhancement.&lt;/p&gt;

&lt;p&gt;Now go out there and write good documentation for your components!&lt;/p&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Have a separate github repo (potentially group as well).&lt;/li&gt;
&lt;li&gt;Have a dedicated homepage&lt;/li&gt;
&lt;li&gt;The default story preview frame should look a little nicer&lt;/li&gt;
&lt;li&gt;Support multiple renderers - discussion in &lt;a href="https://github.com/open-wc/open-wc/issues/1498" rel="noopener noreferrer"&gt;issue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Highlighting of code snippets&lt;/li&gt;
&lt;li&gt;More helpers to be used within stories&lt;/li&gt;
&lt;li&gt;... (feel free to open issues within the corresponding projects)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/openwc" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or follow me on my personal &lt;a href="https://twitter.com/dakmor" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;br&gt;
Make sure to check out our other tools and recommendations at &lt;a href="https://open-wc.org" rel="noopener noreferrer"&gt;open-wc.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://twitter.com/passle_" rel="noopener noreferrer"&gt;Pascal&lt;/a&gt; for feedback and helping turn my scribbles to a followable story.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@aaronburden?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Aaron Burden&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/bible?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>demos</category>
    </item>
    <item>
      <title>Using JavaScript Mixins The Good Parts</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Tue, 18 Feb 2020 12:12:50 +0000</pubDate>
      <link>https://dev.to/open-wc/using-javascript-mixins-the-good-parts-4l60</link>
      <guid>https://dev.to/open-wc/using-javascript-mixins-the-good-parts-4l60</guid>
      <description>&lt;p&gt;Before we can start using something we need to understand what it is and what we can achieve with it. &lt;/p&gt;

&lt;h1&gt;
  
  
  What is a Mixin?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;A mixin is an abstract subclass; i.e. a subclass definition that may be applied to different superclasses to create a related family of modified classes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gilad Bracha and William Cook, &lt;a href="http://www.bracha.org/oopsla90.pdf"&gt;Mixin-based Inheritance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's take for example Logging. Imagine you have 3 Pages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Red&lt;/li&gt;
&lt;li&gt;Green&lt;/li&gt;
&lt;li&gt;Blue
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              +----------+
              |   Page   |
              +----------+
                |  |  |
     +----------+  |  +-----------+
     |             |              |
+---------+ +-----------+ +----------+
| PageRed | | PageGreen | | PageBlue |
+----+----+ +-----------+ +----------+

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageRed&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageGreen&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageBlue&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we want to log whenever someone goes on Page Red.&lt;br&gt;
To achieve that we extend Page Red and make a Logged Page Red.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              +----------+
              |   Page   |
              +-+--+--+--+
                |  |  |
     +----------+  |  +-----------+
     |             |              |
+----+----+  +-----+-----+  +-----+----+
| PageRed |  | PageGreen |  | PageBlue |
+----+----+  +-----------+  +----------+
     |
+----+----+
| Logged  |
| PageRed |
+---------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageRed&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageGreen&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageBlue&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LoggedPagRed&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;PageRed&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we want to start logging for PageGreen we have an issue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we can't put the logic in &lt;code&gt;Page&lt;/code&gt; as Blue should not be logged&lt;/li&gt;
&lt;li&gt;we can't reuse the logic in &lt;code&gt;Logged PageGreen&lt;/code&gt; as we can not extend from 2 sources (even if we could it would mean conflicting info in Red and Green)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What we can do is put it in an "external" place and write it so it can be "mixed in".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               +----------+                +----------+
               |   Page   |                | Logging* |
               +-+--+--+--+                +----------+
                 |  |  |
      +----------+  |  +-----------+
      |             |              |
+-----+----+  +-----+-----+  +-----+----+
| PageRed  |  | PageGreen |  | PageBlue |
|  with    |  |   with    |  +----------+
| Logging* |  |  Logging* |
+----------+  +-----------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// defining the Mixin&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoggingMixin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;superclass&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LoggingMixin&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;superclass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// logging logic&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="c1"&gt;// applying a Mixin&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageRed&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;LoggingMixin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageGreen&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;LoggingMixin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PageBlue&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With that approach we can extract logic into a separate code piece we can use where needed.&lt;/p&gt;

&lt;p&gt;For a more in-depth technical explanation please read &lt;a href="https://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/"&gt;Real Mixins with JavaScript Classes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Deduping of Mixins Necessary?
&lt;/h2&gt;

&lt;p&gt;We now want all logging to the Red, Green, and Blue pages.&lt;br&gt;
Easy enough - as we can now apply the LoggingMixin on the Page itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               +----------+               +----------+
               |   Page   |               | Logging* |
               |   with   |               +----------+
               | Logging* |
               +-+--+--+--+
                 |  |  |
      +----------+  |  +-----------+
      |             |              |
+-----+----+  +-----+-----+  +-----+----+
| PageRed  |  | PageGreen |  | PageBlue |
+----------+  |   with    |  +----------+
              |  Logging* |
              +-----------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;However, Team Green was eager to launch, so they already applied &lt;code&gt;LoggingMixin&lt;/code&gt; to their Page class. When we apply it to the base &lt;code&gt;Page&lt;/code&gt; class, Mixin is now applied twice 😱&lt;br&gt;
Suddenly, the Green page will print each log twice - not what we originally had in mind.&lt;/p&gt;

&lt;p&gt;What we need to do is make sure that each Mixin is attached only once even if we try to apply it multiple times.&lt;/p&gt;

&lt;p&gt;Generally, the more generic a mixin is, the higher the chance becomes that is gets applied more than once. As a mixin author, you can't control how it is used, and can't always predict it. So as a safety measure it is always recommended to create deduping mixins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @open-wc/dedupe-mixin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dedupeMixin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@open-wc/dedupe-mixin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyMixin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dedupeMixin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;superclass&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyMixin&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;superclass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// your mixin code goes here&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see exactly this situation in the demo.&lt;/p&gt;

&lt;p&gt;By applying dedupeMixin to the mixin function, before we export it, we can be sure that our mixin class will only take effect once, even if it is mixed into multiple base classes in the inheritance chain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://open-wc.org/dedupe-mixin/demo/no-dedupe/"&gt;no-dedupe&lt;/a&gt; "fails" by logging Green two times&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://open-wc.org/dedupe-mixin/demo/with-dedupe/"&gt;with-dedupe&lt;/a&gt; "succeeds" by logging Green one time as well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check the source code for both on &lt;a href="https://github.com/open-wc/open-wc/tree/master/packages/dedupe-mixin/demo-typed"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested examples
&lt;/h2&gt;

&lt;p&gt;You may think that the above example is too simple and can be solved by aligning on when to do changes.&lt;br&gt;
However, in most real-life scenarios the situation is much more complicated 🙈&lt;br&gt;
Mixins can be extended and just because you import a class it does not mean that this class has some Mixins pre-applied.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               +----------+               +----------+      +----------+
               |   Page   |               | Logging* |      | Feature  |
               |   with   |               +----+-----+      |   with   |
               | Logging* |                    |            | Metrics* |
               +-+--+--+--+               +----+-----+      +----+--+--+
                 |  |  |                  | Metrics* |           |  |
      +----------+  |  +-----------+      +----------+           |  +------
      |             |              |                             |
+-----+----+  +-----+-----+  +-----+----+                 +------+-------+
| PageRed  |  | PageGreen |  | PageBlue |                 | WaterFeature |
+----------+  +-----------+  |   with   |                 +--------------+
                             | Metrics* |
                             +----------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Pages generally only need Logging&lt;/li&gt;
&lt;li&gt;There is however also more advanced Metrics System which extends Logging&lt;/li&gt;
&lt;li&gt;Metrics was separately developed for Features&lt;/li&gt;
&lt;li&gt;When we now want to get the same Metrics on Page Blue we get duplicate logging without consciously applying logging even once (we do &lt;code&gt;class PageBlue extends MetricsMixin(Page) {}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Only deduping can help in these scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When NOT to use a Mixin
&lt;/h2&gt;

&lt;p&gt;You may think now "dam that is powerful" and you are right. Should we use it now for everything? Hell, no. &lt;/p&gt;

&lt;p&gt;Only use it if you absolutely need access to the instance itself. Here are some bad examples when NOT to use a Mixin.&lt;/p&gt;

&lt;h3&gt;
  
  
  I want to do &lt;code&gt;if (this.isIE11()) { // ... }&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;For any "pure" function it is better to keep it outside of the class/prototype. e.g. better write it like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isIE11&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./helpers.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isIE11&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  I want to have a special &lt;code&gt;this.addEventListener()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Firstly overriding built-in functions can be &lt;em&gt;really&lt;/em&gt; dangerous. When inside your class and when you need to make a very specific use-case happen then it might be ok. However, if that magically happens while using a Mixin it can be very confusing.&lt;/p&gt;

&lt;p&gt;Better to have these as an extra function which can pass on this. That way people can opt these special features without "polluting" their prototype chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;specialAddEventListener&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./helpers.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;specialAddEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Secondly all properties/methods are global to the class/prototype. That means if two mixins use the same name it will clash. Therefore make sure to definitely use specific names for private/protected methods and make sure if you use common names that it is clear from the mixin name/docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use a Mixin
&lt;/h2&gt;

&lt;p&gt;If you need to have access to the class instance itself. Meaning if every instance may have a different setting. &lt;/p&gt;

&lt;p&gt;A valid example could be for example a &lt;code&gt;LocalizeMixin&lt;/code&gt; which enables you to set &lt;code&gt;myEl.locale = 'de-DE';&lt;/code&gt;. In this case, the Mixin needs to provide and react to this property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dedupeMixin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@open-wc/dedupe-mixin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LocalizeMixin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dedupeMixin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;superclass&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LocalizeMixin&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;superclass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// this assumes a Mixin for LitElement&lt;/span&gt;
      &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;locale&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// react to locale change&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  What we have learned
&lt;/h2&gt;

&lt;p&gt;With Mixins you can bring shared logic into multiple classes. It is a very powerful tool - and with that power comes responsibility. Be sure to use it responsibly and to dedupe your mixins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Ascii Graphics made with &lt;a href="http://asciiflow.com/"&gt;AsciiFlow&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
Photo by &lt;a href="https://unsplash.com/@vaniashows?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Vania Shows&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/mixer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>litelement</category>
    </item>
    <item>
      <title>ING open sources Lion: A library of performant, accessible &amp; flexible Web Components</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Thu, 13 Feb 2020 11:19:17 +0000</pubDate>
      <link>https://dev.to/dakmor/ing-open-sources-lion-a-library-of-performant-accessible-flexible-web-components-5bme</link>
      <guid>https://dev.to/dakmor/ing-open-sources-lion-a-library-of-performant-accessible-flexible-web-components-5bme</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on the &lt;a href="https://medium.com/ing-blog/ing-open-sources-lion-a-library-for-performant-accessible-flexible-web-components-22ad165b1d3d"&gt;ING tech blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Web development can be hard, whether you're making your own components, implementing Design Systems, support many different browsers, incorporating accessibility, or adding third party dependencies. Lion aims to make your life a little bit easier, by taking the groundwork of feature complete, accessible, performant, and framework agnostic components out of your hands! Check out the repo at &lt;a href="https://github.com/ing-bank/lion"&gt;ing-bank/lion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As some of you may already know, ING has a long and rich history of building Web Components, starting out with the Polymer library, all the way up to the recently released LitElement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/ing-blog/ing-%EF%B8%8F-web-components-f52aacc71d7a"&gt;Need a refresher?&lt;/a&gt; Web Components are a set of browser standards that allow us to write browser native, reusable, encapsulated, and modular components.&lt;/p&gt;

&lt;p&gt;Web Components standards have finally matured, and today, there are many ways to build and consume your Web Components, like: &lt;a href="https://angular.io/guide/elements"&gt;Angular Elements&lt;/a&gt;, &lt;a href="https://stenciljs.com/docs/introduction/"&gt;Stencil&lt;/a&gt;, &lt;a href="https://vuejsdevelopers.com/2018/05/21/vue-js-web-component/"&gt;Vue&lt;/a&gt;, &lt;a href="https://svelte.dev/"&gt;Svelte&lt;/a&gt; and many, many more. And with the release of the new &lt;a href="https://www.theverge.com/2020/1/15/21066767/microsoft-edge-chromium-new-browser-windows-mac-download-os"&gt;Chromium Edge&lt;/a&gt;, this means that all major browsers support Web Components natively.&lt;/p&gt;

&lt;p&gt;So today, I'm happy to share with you all that ING is open-sourcing its own core library of Web Components: Lion! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Lion?
&lt;/h2&gt;

&lt;p&gt;Imagine the following fictional scenario:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lea is a developer who works at Betatech, and is working on a new in-house application. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Framework Agnostic
&lt;/h3&gt;

&lt;p&gt;Finding components is hard, and can lead to a lot of frustration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lea finds a perfect component for her use case, but unfortunately, it's written in a specific JavaScript framework, and Betatech uses a different framework, so sadly she's not able to use it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, companies have their own visual identities and overriding fully styled components to make them look like the companies identity, can sometimes be more work than start styling from scratch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lea finds a component that offers all the needed functionalities, however, it looks vastly different from Betatechs identity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Customize Functionality
&lt;/h3&gt;

&lt;p&gt;Once you've found your perfect component, at some point down the line you may discover that the component lacks some kind of functionality that you would like to achieve.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lea decides to copy over the code of a component she found, and implement the feature herself, and as a result now has to maintain the entire component as well, which becomes an added burden to the project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;Your components should be usable by every kind of user. Accessibility is hard to get right, but an essential feature, additionally, accessibility issues can be impossible to fix at a later stage without breaking changes, so it is crucial to incorporate accessibility right from the start.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Somewhere down the line, Lea finds an accessibility issue with a component she found online. she can't fix this issue in her code, so she asks the maintainers to help her out. They tell her they can't fix it because they don't want to do a breaking change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or, alternatively&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lea writes her own component, and the accessibility issue is hard to fix because of the way she initially wrote her HTML structure, and will also lead to a breaking change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Does Lea's story feel relatable?&lt;/p&gt;

&lt;p&gt;It highlights multiple common issues in modern web development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finding and adding a dependency is a big deal&lt;/li&gt;
&lt;li&gt;Picking something because of looks alone might not always be the best choice&lt;/li&gt;
&lt;li&gt;Adjusting behavior or styling can be tough to maintain&lt;/li&gt;
&lt;li&gt;If a package is popular it doesn't mean it has good accessibility or performance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What can we do to tackle these issues?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🤔 Imagine having components that purely focus on functionality, and leave the design up to you
&lt;/li&gt;
&lt;li&gt;🤔 Imagine that these components come with great accessibility and performance
&lt;/li&gt;
&lt;li&gt;🤔 Imagine these components are highly extendable and flexible
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now stop imagining, and look at Lion 🦁&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Lion?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;We want to bring the web forward - one component at a time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lion is a white-label, open-source, framework-agnostic component library, and can be the foundation for your codified in-house Design System. Aligning on design and usability is already a tough challenge, with Lion, we hope to take some of the work out of your hands.&lt;/p&gt;

&lt;h3&gt;
  
  
  White Label
&lt;/h3&gt;

&lt;p&gt;Lion is a core package of white label Web Components. What this means is that the core components only consist of very minimal styling, yet has all the fundamental functionalities. White label products are often created so others can easily rebrand them with their own visual identity. This is great because it means that everyone can use the core functionalities of our components while bringing their own branding or Design System because surprisingly, not everyone loves orange.&lt;/p&gt;

&lt;p&gt;And this is exactly what we at ING do as well. Our very own ing-web components extend the Lion components and apply our own ING visual identity which mostly is a thin layer on top of Lion.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="http://lion-web-components.netlify.com/"&gt;Lion demo's&lt;/a&gt;. Looks plain, doesn't it? And now compare Lion to ing-web:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZlQY8irs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/ing-bank/lion/master/demo/stories/side-by-side.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZlQY8irs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/ing-bank/lion/master/demo/stories/side-by-side.png" alt="Lion - ing-web side by side"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Focus
&lt;/h3&gt;

&lt;p&gt;Lion was designed with a focus on global usage and reusability in mind. And as such, the following features were incorporated right from the start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;♻️ Reusability - Our components are meant to be distributed and used on a global scale
&lt;/li&gt;
&lt;li&gt;🚹 Accessibility - Our components are meant to be accessible for everyone
&lt;/li&gt;
&lt;li&gt;🚀 Performance - Our components are meant to be small, performant and fast
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features allow us to roll out our components globally and have a single point of truth. It ensures that everyone in ING has a solid set of building blocks to build their application with, and get up and running in no time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons learned
&lt;/h3&gt;

&lt;p&gt;ING started using web components very early on, and Lion is not the first component library we built. So you may wonder, how are these components different from the previous generation?&lt;/p&gt;

&lt;p&gt;Lion is built from the ground up to allow for accessibility and extendability as we learned that these things are almost impossible to change/achieve at a later point in development. We'd like to highlight a few of the lessons learned while making Lion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not everything has to be a Web Component, for certain functionality regular JavaScript is better.&lt;/li&gt;
&lt;li&gt;Stay as close to native semantic HTML elements as possible.&lt;/li&gt;
&lt;li&gt;Incorporate accessibility from the start.&lt;/li&gt;
&lt;li&gt;Not everything needs to be in the shadow DOM, this is especially important for aria relations and accessibility.&lt;/li&gt;
&lt;li&gt;UI components are hard&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Everyone should use web components but not everyone should write them&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;- &lt;a href="https://twitter.com/erikkroes"&gt;Erik Kroes&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🙋‍ Join us!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adopt Lion!
&lt;/h3&gt;

&lt;p&gt;Lion can help you implement your Design System by providing a white-label, functional, accessible and performant foundation for your component library. All you need to do is bring your own design! So if your company is looking to codify your Design System, Lion makes a great base to start from.&lt;/p&gt;

&lt;p&gt;Alternatively, you could use Lion to create a web component version of your favorite design systems, such as: &lt;a href="https://bulma.io/expo/"&gt;Bulma&lt;/a&gt;, &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt;, &lt;a href="https://material.io/design/"&gt;Material&lt;/a&gt;, &lt;a href="https://boltdesignsystem.com/"&gt;Bolt&lt;/a&gt;, &lt;a href="https://demos.creative-tim.com/vue-argon-design-system/documentation/"&gt;Argon&lt;/a&gt;, &lt;a href="https://tailwindcss.com/"&gt;Tailwind&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, the more users and contributors Lion has, the more stable the Lion base will become, which helps everyone who uses Lion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contribute to Lion!
&lt;/h3&gt;

&lt;p&gt;At the time of writing, Lion is still in beta phase. We'd love to have your feedback before going to a stable release, so: Do you like open source, and do you want to help Lion out? &lt;br&gt;
You can do so by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reporting/fixing issues&lt;/li&gt;
&lt;li&gt;Working on a completely new component (start with an issue for discussion first)&lt;/li&gt;
&lt;li&gt;Improving documentation&lt;/li&gt;
&lt;li&gt;... every contribution counts! Even typo fixes in the docs 🤓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to create a github issue for any feedback or questions you might have.&lt;br&gt;
You can also find us on the Polymer slack in the &lt;a href="https://polymer.slack.com/messages/CJGFWJN9J/convo/CE6D9DN05-1557486154.187100/"&gt;#lion&lt;/a&gt; channel.&lt;/p&gt;

&lt;p&gt;You can join the Polymer slack by visiting &lt;a href="https://www.polymer-project.org/slack-invite"&gt;https://www.polymer-project.org/slack-invite&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  🔨 Extend the components
&lt;/h2&gt;

&lt;p&gt;You can use Lion as the base to implement your own codified Design System.&lt;/p&gt;

&lt;p&gt;Let see how Lea's story would have panned out if she would have chosen Lion instead.&lt;/p&gt;

&lt;p&gt;First, install what you need&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i lit-element @lion/tabs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;lea-tabs&lt;/code&gt; component by reusing the functionality of Lion. This gives Lea all the functionality and accessible core that she needs for her custom tabs component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LionTabs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lion/tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LeaTabs&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;LionTabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
        /* my stylings */
      `&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_setupFeature&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;_setupFeature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// being awesome&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lea-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LeaTabs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lea also wants to be able to style the tab and tab-panel according to Betatechs visual identity. In order to do so, she creates a &lt;code&gt;lea-tab-panel&lt;/code&gt; component and a &lt;code&gt;lea-tab&lt;/code&gt; component, which she can then fully style however she desires, and eventually place inside the &lt;code&gt;lea-tabs&lt;/code&gt; component. You can see how Lea achieved this in the example down below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LeaTab&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`/* my stylings */`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;!-- dom as needed --&amp;gt;
      &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lea-tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LeaTab&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Excellent! Lea can now use the tabs component like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;lea-tabs&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;lea-tab&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Info&lt;span class="nt"&gt;&amp;lt;/lea-tab&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;lea-tab-panel&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Info page with lots of information about us.
  &lt;span class="nt"&gt;&amp;lt;/lea-tab-panel&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;lea-tab&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Work&lt;span class="nt"&gt;&amp;lt;/lea-tab&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;lea-tab-panel&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Work page that showcases our work.
  &lt;span class="nt"&gt;&amp;lt;/lea-tab-panel&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/lea-tabs&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There we go, Lea's component is already done, so let's write some documentation. See the &lt;a href="https://lion-web-components.netlify.com/?path=/docs/intro-tabs-example--default-story"&gt;live Lea tabs documentation page&lt;/a&gt;. You can see the full code of &lt;code&gt;lea-tabs&lt;/code&gt; &lt;a href="https://github.com/ing-bank/lion/tree/master/demo/"&gt;on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;P.S.: Do note that Lea is now responsible for keeping the documentation of &lt;code&gt;lea-tabs&lt;/code&gt; up to date herself, and improvements on Lion's documentation will not automatically be reflected on Lea's documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Open Source?
&lt;/h3&gt;

&lt;p&gt;Component libraries are in huge demand. By open-sourcing our extendable components, we help you to &lt;em&gt;do your thing&lt;/em&gt;, while reaping all the benefits from the open-source community. What's more, is that any contribution made to Lion directly impacts and benefits every design system using it (including ing-web) on a global scale. This means we get the best of both worlds by helping people with our components, and getting valuable contributions from the community!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ing-bank/lion/"&gt;Take a look at our repository!&lt;/a&gt; and if you wanna stay up to date be sure to watch and/or star it - there is no twitter (yet) but you can follow me &lt;a href="https://twitter.com/daKmoR"&gt;Thomas Allmer&lt;/a&gt; - hey there 👋&lt;/p&gt;

&lt;h3&gt;
  
  
  But there's more!
&lt;/h3&gt;

&lt;p&gt;Building applications is hard, and sometimes, you need a little bit more than just the right component alone, but also things like: Validation, Forms, Overlays, Localization, etc. But fear not; Lion has got you covered!&lt;/p&gt;

&lt;p&gt;You can check them out in our &lt;a href="http://lion-web-components.netlify.com/"&gt;documentation&lt;/a&gt;, and we'll go into more depth about Lion's additional systems in future blog posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Finally, we'd like to close this announcement by saying a few thank yous:&lt;/p&gt;

&lt;p&gt;ING, for giving us the opportunity to work on this project, and make it great together with the open-source community.&lt;/p&gt;

&lt;p&gt;Everyone who has worked on Lion (especially the Lion team), including all the contributors (36 contributors and counting!).&lt;/p&gt;

&lt;p&gt;And last but not least &lt;a href="https://twitter.com/passle_"&gt;Pascal Schilp&lt;/a&gt;, for turning my scribbles into a followable story.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>javascript</category>
      <category>designsystem</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Storybook for web components on steroids</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Thu, 28 Nov 2019 20:13:42 +0000</pubDate>
      <link>https://dev.to/open-wc/storybook-for-web-components-on-steroids-4h29</link>
      <guid>https://dev.to/open-wc/storybook-for-web-components-on-steroids-4h29</guid>
      <description>&lt;p&gt;Building a web application is quite a big and challenging task.&lt;br&gt;
As with many big tasks, it makes sense to break them into smaller pieces.&lt;br&gt;
For applications, this usually means splitting your application into multiple separate components.&lt;/p&gt;

&lt;p&gt;Once you start doing that you will notice that you have a lot of individual pieces in your hands, and that it can be tough to keep an overview of all these moving parts.&lt;/p&gt;

&lt;p&gt;To solve this we have been recommending &lt;a href="https://storybook.js.org/"&gt;storybook&lt;/a&gt; since quite some time.&lt;/p&gt;

&lt;p&gt;The support for web components has always been good (via &lt;code&gt;@storybook/polymer&lt;/code&gt;), and it got even better with the recently added &lt;code&gt;@storybook/web-components&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are however some parts in storybook which are not fine-tuned for developing web components (the open-wc way).&lt;/p&gt;

&lt;p&gt;Let's look at some of those points and how we can improve them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can follow along in the &lt;a href="https://github.com/open-wc/blog-posts/tree/master/2019-11-storybook-for-web-components-on-steroids"&gt;accompanying github repo&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After a typical storybook setup it looks like &lt;a href="https://github.com/open-wc/blog-posts/tree/master/2019-11-storybook-for-web-components-on-steroids/storybook"&gt;this&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;start-storybook
info @storybook/web-components v5.3.0-alpha.40
info
info &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Loading presets
info &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Loading presets
info &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Loading custom manager config.
info &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Using default Webpack setup.
webpack built b6c5b0bf4e5f02d4df8c &lt;span class="k"&gt;in &lt;/span&gt;7853ms
╭───────────────────────────────────────────────────╮
│                                                   │
│   Storybook 5.3.0-alpha.40 started                │
│   8.99 s &lt;span class="k"&gt;for &lt;/span&gt;manager and 8.53 s &lt;span class="k"&gt;for &lt;/span&gt;preview       │
│                                                   │
│    Local:            http://localhost:52796/      │
│    On your network:  http://192.168.1.5:52796/    │
│                                                   │
╰───────────────────────────────────────────────────╯
&lt;span class="c"&gt;# browser opens&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When we compare this to starting a project with &lt;code&gt;npm init @open-wc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run start
es-dev-server started on http://localhost:8000
  Serving files from &lt;span class="s1"&gt;'/my-demo'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  Opening browser on &lt;span class="s1"&gt;'/my-demo/'&lt;/span&gt;
  Using &lt;span class="nb"&gt;history &lt;/span&gt;API fallback, redirecting non-file requests to &lt;span class="s1"&gt;'/my-demo/index.html'&lt;/span&gt;
&lt;span class="c"&gt;# browser opens&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The most obvious difference is that in one case we have 2 builds of ~8 seconds and in the other, we don't have any.&lt;/p&gt;

&lt;p&gt;So why there are 2 builds?&lt;/p&gt;

&lt;p&gt;To get an idea about why this might be needed we first need to understand some of the requirements of a universal demo system like storybook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Excursion universal demo system
&lt;/h2&gt;

&lt;p&gt;Let's assume we are a startup and we are creating a new app.&lt;br&gt;
Our choice of technology is &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt;. We happily start building our app and soon we see the need of having a demo system to show and work on all these individual components. Go forth they said and we built a demo system for vue.&lt;/p&gt;

&lt;p&gt;It could look something like this&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;just some example code - don't read this as good vue code 😅&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{ msg }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"demo in demos"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"showDemo(demo.name)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{demo.name}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-html=&lt;/span&gt;&lt;span class="s"&gt;"demo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HelloWorld&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Demo System&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;demos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;Hey there from demo one&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;I am demo two&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;showDemo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demoIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;demoIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demoIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demoIndex&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;Please select a demo by clicking in the menu&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The code here shows only the most relevant information&lt;br&gt;
For a demo and more details look in the &lt;a href="https://github.com/open-wc/blog-posts/tree/master/2019-11-storybook-for-web-components-on-steroids/vue-demo-system"&gt;vue-demo-system&lt;/a&gt; folder&lt;br&gt;
You can start it via &lt;code&gt;npm i &amp;amp;&amp;amp; npm run serve&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everything works, everyone is happy - life is good.&lt;/p&gt;

&lt;p&gt;Fast forward 12 months and we got a new CIO. A new wind is blowing and with it a prosperous opportunity to work on a second app. The breeze, however, demands that this time it is written in Angular. No, problem - we are professionals and off we go working on the new app.&lt;br&gt;
Pretty early we see a similar pattern as before - components everywhere and we need a way to work and demo them individually.&lt;br&gt;
Ah we think that's easy we already have a system for that 😬&lt;/p&gt;

&lt;p&gt;We give our best - but the angular components just don't wanna work well together with the vue demo app 😭.&lt;/p&gt;

&lt;p&gt;What can we do? Do we really need to recreate the demo system for Angular now?&lt;/p&gt;

&lt;p&gt;It seems our issue is that having the demo UI and the component demo on the same page has the unwanted side effect that we can only use the UI system within our demos.&lt;br&gt;
Not very universal that is 😅&lt;br&gt;
Could we split the UI and the demo?&lt;/p&gt;

&lt;p&gt;How about using iframes and only communicate via postMessage?&lt;br&gt;
Would that mean each window can do what they want? 🤞&lt;/p&gt;

&lt;p&gt;Let's make a simple POC (proof of concept) with&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a ul/li list as a menu&lt;/li&gt;
&lt;li&gt;an iframe to show the demo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What we need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We start with an empty menu&lt;/li&gt;
&lt;li&gt;We listen to post messages of demos&lt;/li&gt;
&lt;li&gt;The iframe gets loaded and the demos inside fires post messages&lt;/li&gt;
&lt;li&gt;We then create menu items for each demo&lt;/li&gt;
&lt;li&gt;On click on the menu item, we change the iframe url&lt;/li&gt;
&lt;li&gt;If the iframe gets a demo to show it updates the html&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the &lt;code&gt;index.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"iframe"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./iframe.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`./iframe.html?slug=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here is the &lt;code&gt;iframe.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Please select a demo by clicking in the menu&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// Demo One&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;Hey there from demo two&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Demo Two&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;I am demo two&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// register demos when not currently showing a demo&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The code here shows only the most relevant information&lt;br&gt;
For a demo and more details look in the &lt;a href="https://github.com/open-wc/blog-posts/tree/master/2019-11-storybook-for-web-components-on-steroids/postMessage"&gt;postMessage&lt;/a&gt; folder&lt;br&gt;
You can start it via &lt;code&gt;npm i &amp;amp;&amp;amp; npm run start&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now imagine that the UI is way more than just a ul/li list and that the demo follows a certain demo format?&lt;br&gt;
Could this be a system which allows the UI and the demo to be written in completely different technologies?&lt;/p&gt;

&lt;p&gt;The answer is YES 💪&lt;/p&gt;

&lt;p&gt;The only means of communication is done via postMessages.&lt;br&gt;
Therefore the preview only needs to know which postMessage format to use.&lt;br&gt;
Also, postMessage is a native function so every framework or system can use them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two builds (continued)
&lt;/h2&gt;

&lt;p&gt;The above concept is what is used by storybook - which means that there are actually 2 applications being run.&lt;br&gt;
One is the storybook UI (called manager) and one is your actual demo (called preview).&lt;br&gt;
Knowing that it makes sense that there are 2 separate builds.&lt;/p&gt;

&lt;p&gt;But why is there a build step at all? Why would storybook have such setup?&lt;/p&gt;

&lt;p&gt;Let's see what is needed to allow for some code to be run and worked on in multiple browsers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Excursion shipping code based on browser capabilities
&lt;/h2&gt;

&lt;p&gt;Let's have a small example where we are using &lt;a href="https://github.com/tc39/proposal-class-fields"&gt;private class fields&lt;/a&gt;.&lt;br&gt;
This feature is currently at stage 3 and only available in Chrome.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./MyClass.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;inst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// MyClass.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;privateField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Class with a private field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;publicMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;privateField&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We deliberately put a debugger breakpoint in there to see the actual code the browser is executing.&lt;/p&gt;

&lt;p&gt;Let's see how webpack with a few babel plugins handles it. (&lt;a href="https://github.com/open-wc/blog-posts/tree/master/2019-11-storybook-for-web-components-on-steroids/EsDevServer-vs-WebpackDevServer/webpack.config.js"&gt;see full config&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;__webpack_require__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__webpack_exports__&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cm"&gt;/* harmony export (binding) */&lt;/span&gt; &lt;span class="nx"&gt;__webpack_require__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__webpack_exports__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MyClass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;_classCallCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Constructor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Constructor&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cannot call a class as a function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;_defineProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// ... more helper functions&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="cm"&gt;/*#__PURE__*/&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;_classCallCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;_privateField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Class with a private field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;_createClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publicMethod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;publicMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_classPrivateFieldGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_privateField&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}();&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_privateField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Wow that is quite some code 🙈 and it does not really look like the code written 😱&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: in most cases you will not see this because of source maps&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What happened? in a typical webpack &amp;amp; babel setup your code gets compiled down to es5 in order to be able to run the code also on older browser like IE11.&lt;/p&gt;

&lt;p&gt;However, you may ask how often do I actually run my app in an older browser?&lt;/p&gt;

&lt;p&gt;A typical developer should probably develop ~90% on a modern browser and ~10% on older browsers to make sure everything still works in order.&lt;br&gt;
At least we hope you have such a nice workflow 🤗&lt;/p&gt;

&lt;p&gt;So the question is why compile, ship, debug and work with this "strange" code 100% of the time if it's only needed for 10%?&lt;br&gt;
Could we do better?&lt;/p&gt;

&lt;p&gt;Let's see how &lt;code&gt;es-dev-server&lt;/code&gt; handles it by opening the same file on chrome.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;privateField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Class with a private field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;publicMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;privateField&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It looks exactly as the original code - because it is. The code as is was fully capable of running in chrome without any adjustments.&lt;br&gt;
And that's what is happening it ships the source as is.&lt;/p&gt;

&lt;p&gt;However, we are using private class fields which is an unsupported feature for example on Firefox.&lt;br&gt;
What happens if we open it there?&lt;/p&gt;

&lt;p&gt;it fails 😭&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SyntaxError: private fields are not currently supported&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ok, it's our fault as we are using a stage 3 feature and are not doing any compilation now.&lt;/p&gt;

&lt;p&gt;Let's try it with &lt;code&gt;es-dev-server --babel&lt;/code&gt; which in turn will use the same &lt;code&gt;.babelrc&lt;/code&gt; as webpack.&lt;/p&gt;

&lt;p&gt;The following code will be generated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;_classPrivateFieldGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;privateMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;descriptor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;privateMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attempted to get private field on non-instance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;_privateField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Class with a private field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;publicMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_classPrivateFieldGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_privateField&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_privateField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And it works 💪&lt;br&gt;
It only compiles the private fields and not everything 👌&lt;/p&gt;

&lt;p&gt;However, if you now go back to chrome you will see that it is now compiled there as well.&lt;br&gt;
The reason for it is that once you start going through babel it just does it's thing based on &lt;code&gt;@babel/preset-env&lt;/code&gt; and babel is always on the conservative side.&lt;/p&gt;

&lt;p&gt;The real magic ✨ happens when you open it on an older browser like IE11.&lt;br&gt;
As then it will compile it down to &lt;a href="https://github.com/systemjs/systemjs"&gt;systemjs&lt;/a&gt;, a polyfill for es modules.&lt;/p&gt;

&lt;p&gt;It will look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_export&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_privateField&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;_classCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Constructor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It will behave exactly like real es modules, so that your code will work just fine on browsers which don't support them 💪&lt;/p&gt;

&lt;p&gt;If you are concerned about speed it is best to only rely on stage 4 features and to not use babel at all.&lt;br&gt;
You can if really needed use 2 start commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"start": "es-dev-server --open",
"start:babel": "es-dev-server --babel --open",
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So what es-dev-server auto mode enables is that you don't need to think about it.&lt;br&gt;
It will be instant on modern browsers and will even work in these moments where you have a need to test in older browsers.&lt;/p&gt;

&lt;p&gt;To summarize in order to be able to work with and debug code in all the browser we want to support we basically have 2 options.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compile down to the lowest denominator&lt;/li&gt;
&lt;li&gt;Serve code base on browser capabilities&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And as always please don't go crazy with new features.&lt;br&gt;
Use what is currently stable and available on your development browser.&lt;br&gt;
You will have the best experience when you do not use a custom babel config.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The code here shows only the most relevant information&lt;br&gt;
For a demo and more details look in the &lt;a href="https://github.com/open-wc/blog-posts/tree/master/2019-11-storybook-for-web-components-on-steroids/EsDevServer-vs-WebpackDevServer"&gt;EsDevServer-vs-WebpackDevServer&lt;/a&gt; folder&lt;br&gt;
You can start it via &lt;code&gt;npm run start&lt;/code&gt;, &lt;code&gt;npm run start:babel&lt;/code&gt; and &lt;code&gt;npm run webpack&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Source maps
&lt;/h2&gt;

&lt;p&gt;Luckily in most cases, even when working with compiled code you will see the source code.&lt;br&gt;
How is that possible? It's all thanks to &lt;a href="https://blog.teamtreehouse.com/introduction-source-maps"&gt;Sourcemaps&lt;/a&gt;.&lt;br&gt;
They are a way to map the original code to the compiled code and browser are smart enough to link them together and only show you what you are interested in.&lt;br&gt;
As long as the option "Enable JavaScript source maps" is checked in your dev tools.&lt;/p&gt;

&lt;p&gt;It is really awesome that it justs works. It is however yet another moving part that may break or you need to know about it at least.&lt;/p&gt;
&lt;h2&gt;
  
  
  Opportunity
&lt;/h2&gt;

&lt;p&gt;So looking at compilation and shipping of modern code we see a window of opportunity.&lt;br&gt;
We want to have the features of storybook but we also want to have the ease of use of not relying on webpack.&lt;/p&gt;

&lt;p&gt;In short, the idea is to marry storybook ui with es-dev-server.&lt;/p&gt;

&lt;p&gt;Let's get started 💪&lt;/p&gt;

&lt;p&gt;Here is the master plan&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prebuild storybook ui (so we are not forced to use webpack)&lt;/li&gt;
&lt;li&gt;Replace webpack magic like &lt;code&gt;require.context&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mimic how the preview communicates with the manager&lt;/li&gt;
&lt;li&gt;Use rollup to build a static version of storybook&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Storybook on steroids
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Prebuild storybook
&lt;/h3&gt;

&lt;p&gt;In order to get an es module version of the storybook preview, it needs to go through webpack &amp;amp; rollup.&lt;br&gt;
Yes, it is a little black magic but that was the only way that worked.&lt;br&gt;
It seems storybook is not yet optimized to have a fully separated manager/preview.&lt;br&gt;
But hey it works and we will collaborate with storybook to make this even better 💪&lt;/p&gt;

&lt;p&gt;You can find the source &lt;a href="https://github.com/open-wc/storybook-prebuilt"&gt;on github&lt;/a&gt; and the output is published on &lt;a href="https://www.npmjs.com/package/@open-wc/storybook-prebuilt"&gt;npm as @open-wc/storybook-prebuilt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Prebuilt has the following benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast&lt;/li&gt;
&lt;li&gt;preview can be independent of storybooks build setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prebuilt has the following downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can not change the addons of a prebuilt&lt;/li&gt;
&lt;li&gt;you can, however, create your own prebuilt&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Replace webpack magic
&lt;/h3&gt;

&lt;p&gt;In the current storybook &lt;code&gt;require.context&lt;/code&gt; is used in &lt;code&gt;preview.js&lt;/code&gt; to define which stories are loaded.&lt;br&gt;
This is, however, a feature only available in &lt;code&gt;webpack&lt;/code&gt; which basically means it is a lock in to a specific build tool.&lt;br&gt;
We would like to free ourself to choose whatever we want so this needs to be replaced.&lt;/p&gt;

&lt;p&gt;We opted for a command-line argument.&lt;/p&gt;

&lt;p&gt;In short instead of defining where to look for stories in your js you now do it on the command line via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;start-storybook &lt;span class="nt"&gt;--stories&lt;/span&gt; &lt;span class="s1"&gt;'path/to/stories/*.stories.{js,mdx}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Doing so allows exposing this value to various tools like &lt;code&gt;koa-middlewares&lt;/code&gt; and &lt;code&gt;rollup&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mimic how the preview communicates with the manager
&lt;/h3&gt;

&lt;p&gt;Now that we can "include/use" the storybook UI (manager) independent it's time to spin up &lt;code&gt;es-dev-server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the manager, we create an &lt;code&gt;index.html&lt;/code&gt; which boils down to a single import&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"path/to/node_modules/@open-wc/storybook-prebuilt/dist/manager.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We do some special caching to make sure your browser only ever loads the storybook manager once.&lt;/p&gt;

&lt;p&gt;For the preview, it is a little more as we need to load/register all the individual stories as shown in the postMessage example.&lt;br&gt;
The list of stories we will get via the command line argument.&lt;/p&gt;

&lt;p&gt;The important bits which end up being used by the browser is a dynamic import of all story files and then calling storybooks configure which will trigger a postMessage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configure&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./node_modules/@open-wc/demoing-storybook/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/stories/demo-wc-card.stories.mdx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;// here an import to every story file will created&lt;/span&gt;
&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Extra mdx support
&lt;/h4&gt;

&lt;p&gt;The upcoming storybook 5.3.x (currently in beta) will introduce docs mode.&lt;br&gt;
A special mode which allows writing markdown together with stories in a single file and it will be displayed on a single page.&lt;br&gt;
You can think of it as Markdown but on steroids 😬&lt;/p&gt;

&lt;p&gt;The format is called mdx and allows to write markdown but also to import javascript and write jsx.&lt;/p&gt;

&lt;p&gt;We recommend it as the primary way to write documentation about your components.&lt;/p&gt;

&lt;p&gt;In order to support such a feature es-dev-server needs to understand how to handle an mdx file.&lt;/p&gt;

&lt;p&gt;For that, we added a &lt;a href="https://github.com/open-wc/open-wc/blob/demoing-storybook%401.0.9/packages/demoing-storybook/src/shared/createMdxToJsTransformer.js"&gt;koa middleware&lt;/a&gt; which converts requests to &lt;code&gt;*.mdx&lt;/code&gt; files into the &lt;a href="https://storybook.js.org/docs/formats/component-story-format/"&gt;CSF&lt;/a&gt; (Component Story Format).&lt;/p&gt;

&lt;p&gt;It basically means when you request &lt;code&gt;http://localhost:8001/stories/demo-wc-card.stories.mdx&lt;/code&gt; and the file look like this on the file system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;###### Header&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;Story&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Custom Header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {html&lt;span class="sb"&gt;`
    &amp;lt;demo-wc-card header="Harry Potter"&amp;gt;A character that is part of a book series...&amp;lt;/demo-wc-card&amp;gt;
  `&lt;/span&gt;}
&lt;span class="nt"&gt;&amp;lt;/Story&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;it will server this to your browser&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nx"&gt;mdx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Header`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;demo-wc-card header="Harry Potter"&amp;gt;A character that is part of a book series...&amp;lt;/demo-wc-card&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;customHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;customHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom Header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;customHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mdxSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html`&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;    &amp;lt;demo-wc-card header="Harry Potter"&amp;gt;A character that is part of a book series...&amp;lt;/demo-wc-card&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;  `&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can just open your Network Panel and look at the response 💪&lt;/p&gt;

&lt;h3&gt;
  
  
  Use rollup to build a static storybook
&lt;/h3&gt;

&lt;p&gt;In most cases, you will also want to publish your storybook somewhere on a static server.&lt;br&gt;
For that, we pre-setup a rollup configuration and which does all of the above and outputs 2 versions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;for modern browsers who support es modules and&lt;/li&gt;
&lt;li&gt;for all other browsers we ship an es5 version with all polyfills&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more details on how the different versions are shipped from a static server please see the &lt;a href="https://open-wc.org/building/building-rollup.html#configuration"&gt;open-wc rollup recommendation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The code here shows only the most relevant information&lt;br&gt;
For a full demo see the &lt;a href="https://github.com/open-wc/blog-posts/tree/master/2019-11-storybook-for-web-components-on-steroids/storybookOnSteroids"&gt;storybookOnSteroids&lt;/a&gt; folder&lt;br&gt;
You can start it via &lt;code&gt;npm i &amp;amp;&amp;amp; npm run storybook&lt;/code&gt;&lt;br&gt;
For the actual source code see &lt;a href="https://github.com/open-wc/open-wc/tree/master/packages/demoing-storybook"&gt;@open-wc/demoing-storybook&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;We did it 💪&lt;/p&gt;

&lt;p&gt;A fully-featured demo system that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is buildless on modern browsers&lt;/li&gt;
&lt;li&gt;starts up lightning-fast&lt;/li&gt;
&lt;li&gt;has a prebuilt UI&lt;/li&gt;
&lt;li&gt;serves preview code based on browser capabilities&lt;/li&gt;
&lt;li&gt;uses &lt;code&gt;es-dev-server&lt;/code&gt; under the hood so you can use all its features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And above all, it's just wonderful to see how a completely separate server can power storybook.&lt;br&gt;
The storybook setup is really worth it 👍&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can check it out in the &lt;a href="https://github.com/open-wc/open-wc/tree/master/packages/demoing-storybook"&gt;open-wc repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;See a live example at &lt;a href="https://open-wc.org/demoing-storybook/"&gt;https://open-wc.org/demoing-storybook/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;And read the documentation &lt;a href="https://open-wc.org/demoing/"&gt;documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PS: it's not all roses and rainbows but with that step, we now know that it is possible - further improvements like a smaller preview bundle or separate packages for the mdx transformation will happen at some point 🤗&lt;/p&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;We hope that this can be a starting point so storybook can directly support other framework servers as well 👍&lt;br&gt;
Even non JavaScript servers could be possible - Ruby, PHP are you ready? 🤗&lt;/p&gt;

&lt;p&gt;If you are interested in supporting your frameworks server and you need help/guidance be sure to let us know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/openwc"&gt;Twitter&lt;/a&gt;, or follow me on my personal &lt;a href="https://twitter.com/dakmor"&gt;Twitter&lt;/a&gt;.&lt;br&gt;
Make sure to check out our other tools and recommendations at &lt;a href="https://open-wc.org"&gt;open-wc.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://dev.to/bennypowers"&gt;Benny&lt;/a&gt; and &lt;a href="https://github.com/LarsDenBakker"&gt;Lars&lt;/a&gt; for feedback and helping turn my scribbles to a followable story.&lt;/p&gt;

&lt;p&gt;Cover Photo by &lt;a href="https://unsplash.com/@californong"&gt;Nong Vang&lt;/a&gt; on &lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>storybook</category>
      <category>demos</category>
    </item>
    <item>
      <title>Generating TypeScript Definition Files from JavaScript</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Wed, 04 Sep 2019 09:44:52 +0000</pubDate>
      <link>https://dev.to/open-wc/generating-typescript-definition-files-from-javascript-5bp2</link>
      <guid>https://dev.to/open-wc/generating-typescript-definition-files-from-javascript-5bp2</guid>
      <description>&lt;p&gt;At &lt;a href="https://open-wc.org" rel="noopener noreferrer"&gt;open-wc&lt;/a&gt;, we are big fans of &lt;a href="https://dev.to/open-wc/on-the-bleeding-edge-3cb8"&gt;buildless&lt;/a&gt; development setups. We have &lt;a href="https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao"&gt;a post&lt;/a&gt; or &lt;a href="https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5"&gt;two&lt;/a&gt; about it 😄. &lt;a href="https://open-wc.org/about/rationales.html" rel="noopener noreferrer"&gt;We believe&lt;/a&gt; that the future is all about coming back to the web platform. That means relying on native browser features in preference to userland or JavaScript solutions or development tools. That's why we have made it our mission to provide you the developer with the &lt;a href="https://open-wc.org/developing/" rel="noopener noreferrer"&gt;tools&lt;/a&gt; and &lt;a href="https://open-wc.org/testing/" rel="noopener noreferrer"&gt;techniques&lt;/a&gt; to use the platform &lt;em&gt;today&lt;/em&gt;, even before legacy browsers are finally dropped.&lt;/p&gt;

&lt;p&gt;This approach grants us tremendous advantages in DX, performance, and accessibility, but there are drawbacks. JavaScript, famously, is dynamically typed. Developers who want to enjoy type checking at development time will typically reach for Microsoft's TypeScript, Facebook's Flow, or Google's Clojure compiler. All of these require a build step.&lt;/p&gt;

&lt;p&gt;Can we enjoy a safely typed developer experience while "staying true" to the web platform? Let's first dive in and see what Types can give us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples in TypeScript
&lt;/h2&gt;

&lt;p&gt;Let's say we want a function which takes a number or string and returns the square.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// helpers.test.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;square&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our function's TypeScript implementation might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// helpers.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know what you're thinking: a string as an argument? While implementing, we discovered that that was a bad idea, too.&lt;/p&gt;

&lt;p&gt;Thanks to the type safety of TypeScript, and the mature ecosystem of developer tools surrounding it like IDE support, we can tell before we even run our tests that &lt;code&gt;square('two')&lt;/code&gt; will not work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjb55jefsw8lzb8pwdzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjb55jefsw8lzb8pwdzd.png" alt="Screenshot of the source code of helpers.test.ts in Microsoft's Visual Studio Code editor, clearly showing an error signal on line 3, where the function square is called with a string as the argument" width="368" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we run the TypeScript compiler &lt;code&gt;tsc&lt;/code&gt; on our files, we'll see the same error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; typescript
&lt;span class="nv"&gt;$ &lt;/span&gt;npx tsc
helpers.tests.ts:8:19 - error TS2345: Argument of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'"two"'&lt;/span&gt; is not assignable to parameter of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'number'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

8     expect&lt;span class="o"&gt;(&lt;/span&gt;square&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'two'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;.to.equal&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    ~~~~~

Found 1 error.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type safety helped us catch this error before we pushed it to production. How can we accomplish this kind of type safety without using TypeScript as a build step?&lt;/p&gt;

&lt;h2&gt;
  
  
  Achieving Type Safety in Vanilla JavaScript
&lt;/h2&gt;

&lt;p&gt;Our first step will be to rename our files from &lt;code&gt;.ts&lt;/code&gt; to &lt;code&gt;.js&lt;/code&gt;. Then we will use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import" rel="noopener noreferrer"&gt;browser-friendly import statements&lt;/a&gt; in our JavaScript files by using relative urls with &lt;code&gt;.js&lt;/code&gt; file extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// helpers.test.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;square&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../helpers.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we will refactor our TypeScript function to JavaScript by stripping out the explicit type checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// helpers.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if we go back to our test file, we no longer see the error at &lt;code&gt;square('two')&lt;/code&gt;, when we pass the wrong type (string) to the function 😭!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fearhb50rx97s8qt4naj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fearhb50rx97s8qt4naj2.png" alt="In the JavaScript version of the test file, Visual Studio Code no longer shows the error on line 3 when string is called with a string" width="349" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're thinking "Oh well, JavaScript is dynamically typed, there's nothing to be done about it", then check this out: we actually can achieve type safety in vanilla JavaScript, using JSDoc comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Types to JavaScript Using JSDoc
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://jsdoc.app/" rel="noopener noreferrer"&gt;JSDoc&lt;/a&gt; is a long-standing inline documentation format for JavaScript. Typically, you might use it to automatically generate documentation for your server's API or your &lt;a href="https://github.com/runem/web-component-analyzer" rel="noopener noreferrer"&gt;web component's attributes&lt;/a&gt;. Today, we're going to use it to achieve type safety in our editor.&lt;/p&gt;

&lt;p&gt;First, add a JSDoc comment to your function. The docblockr plugin for &lt;a href="https://marketplace.visualstudio.com/items?itemName=jeremyljackson.vs-docblock" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt; and &lt;a href="https://atom.io/packages/docblockr" rel="noopener noreferrer"&gt;atom&lt;/a&gt; can help you do this quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * The square of a number
 * @param {number} number
 * @return {number}
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll configure the TypeScript compiler to check JavaScript files as well as TypeScript files, by adding a &lt;code&gt;tsconfig.json&lt;/code&gt; to our project's root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"es2017"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checkJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitThis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alwaysStrict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mocha"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Hey! I thought you said we weren't going to be using TypeScript here?!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You're right, although we will be authoring and publishing browser-standard JavaScript, our editor tools will be using the &lt;a href="https://github.com/theia-ide/typescript-language-server" rel="noopener noreferrer"&gt;TypeScript Language Server&lt;/a&gt; under the hood to provide us with type-checking.&lt;br&gt;
Doing this allows us to get exactly the same behaviour in VSCode and Atom as with TypeScript.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr7axbui8c3lyitus0or.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr7axbui8c3lyitus0or.png" alt="Screenshot of VSCode showing the same type-checking as in the first figure, but using the annotated JavaScript files" width="356" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We even get the same behaviour when running &lt;code&gt;tsc&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx tsc
&lt;span class="nb"&gt;test&lt;/span&gt;/helpers.tests.js:8:19 - error TS2345: Argument of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'"two"'&lt;/span&gt; is not assignable to parameter of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'number'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

8     expect&lt;span class="o"&gt;(&lt;/span&gt;square&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'two'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;.to.equal&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    ~~~~~

Found 1 error.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Refactoring
&lt;/h2&gt;

&lt;p&gt;Great, we've written our &lt;code&gt;square&lt;/code&gt; feature, including type checks, and pushed it to production. But some time later, the product team came to us saying that an important customer wants to be able to increment the numbers we square for them before we apply the power. This time, the product team already spoke with QA, who worked through the night to provide the following tests for our refactored feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ten&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it appears that they probably should have spent those hours sleeping, as our original typecasting bug is still there.&lt;/p&gt;

&lt;p&gt;How can we deliver this critical (😉) feature to our customers quickly while still maintaining type safety?&lt;/p&gt;

&lt;p&gt;If we had implemented the feature in TypeScript, you might be surprised to learn that we don't need to add explicit type annotations to the second parameter, since we will supply it with a default value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provided default value let's TypeScript statically analyse the code to &lt;em&gt;infer&lt;/em&gt; values type.&lt;/p&gt;

&lt;p&gt;We can get the same effect using our vanilla-js-and-jsdoc production implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * The square of a number
 * @param {number} number
 * @return {number}
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both cases, &lt;code&gt;tsc&lt;/code&gt; will give the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;test&lt;/span&gt;/helpers.tests.js:13:22 - error TS2345: Argument of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'"ten"'&lt;/span&gt; is not assignable to parameter of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'number'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

13     expect&lt;span class="o"&gt;(&lt;/span&gt;square&lt;span class="o"&gt;(&lt;/span&gt;2, &lt;span class="s1"&gt;'ten'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;.to.equal&lt;span class="o"&gt;(&lt;/span&gt;14&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        ~~~~~
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also in both cases, the only thing we needed to add was &lt;code&gt;offset = 0&lt;/code&gt; as it contains the type information already. If we wanted to add an explicit type definition, we could have added a second &lt;code&gt;@param {number} offset&lt;/code&gt; annotation, but for our purposes, this was unnecessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing a Library
&lt;/h2&gt;

&lt;p&gt;If you want people to be able to use your code, you're going to need to publish it at some point. For JavaScript and TypeScript, that typically means &lt;code&gt;npm&lt;/code&gt;.&lt;br&gt;
You will also want to provide your users with the same editor-level type safety that you've been enjoying.&lt;br&gt;
To accomplish that, you can publish Type Declaration files (&lt;code&gt;*.d.ts&lt;/code&gt;)in the root directory of the package you are publishing. TypeScript and the TypeScript Language Sever will respect those declaration files by default whenever they are found in a project's &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;For TypeScript files, this is straightforward, we just add these options to &lt;code&gt;tsconfig.json&lt;/code&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"declaration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and TypeScript will generate &lt;code&gt;*.js&lt;/code&gt; and &lt;code&gt;*.d.ts&lt;/code&gt; files for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// helpers.d.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// helpers.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note that the output of the &lt;code&gt;js&lt;/code&gt; file is exactly the same we wrote in our js version.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing JavaScript Libraries
&lt;/h3&gt;

&lt;p&gt;Sadly, as of now &lt;code&gt;tsc&lt;/code&gt; does not support generating &lt;code&gt;*.d.ts&lt;/code&gt; files from JSDoc annotated files.&lt;br&gt;
We hope it will in the future, and in fact, the original &lt;a href="https://github.com/microsoft/TypeScript/issues/7546" rel="noopener noreferrer"&gt;issue&lt;/a&gt; for the feature is still active, and it seems to be on board for &lt;code&gt;3.7&lt;/code&gt;. Don't take our word for it, the &lt;a href="https://github.com/microsoft/TypeScript/pull/32372" rel="noopener noreferrer"&gt;Pull Request&lt;/a&gt; is in flight.&lt;/p&gt;

&lt;p&gt;In fact, this works so well that we are using it in production for &lt;a href="https://github.com/open-wc/open-wc/blob/master/package.json#L7" rel="noopener noreferrer"&gt;open-wc&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;!WARNING!&lt;br&gt;
This is an unsupported version =&amp;gt; if something does not work no one is going to fix it.&lt;br&gt;
Therefore if your use-case is not supported you will need to wait for the official release of TypeScript to support it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We took the liberty of publishing a forked version &lt;a href="https://www.npmjs.com/package/typescript-temporary-fork-for-jsdoc" rel="noopener noreferrer"&gt;typescript-temporary-fork-for-jsdoc&lt;/a&gt; which is just a copy of the above pull request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Generate TypeScript Definition Files for JSDoc Annotated JavaScript
&lt;/h2&gt;

&lt;p&gt;So now that we have all the information. Let's make it work 💪!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write your code in JS and apply JSDoc where needed&lt;/li&gt;
&lt;li&gt;Use the forked TypeScript &lt;code&gt;npm i -D typescript-temporary-fork-for-jsdoc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Have a &lt;code&gt;tsconfig.json&lt;/code&gt; with at least the following:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allowJs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkJs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Do "type linting" via &lt;code&gt;tsc&lt;/code&gt;, ideally in a &lt;code&gt;pre-commit&lt;/code&gt; hook via &lt;a href="https://github.com/typicode/husky" rel="noopener noreferrer"&gt;husky&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Have &lt;code&gt;tsconfig.build.json&lt;/code&gt; with at least&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noEmit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;declaration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allowJs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkJs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emitDeclarationOnly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Generate Types via &lt;code&gt;tsc -p tsconfig.build.types.json&lt;/code&gt;, ideally in CI&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Publish both your &lt;code&gt;&lt;em&gt;.js&lt;/em&gt;&lt;/code&gt; and &lt;code&gt;.d.ts&lt;/code&gt; files&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;We have exactly this setup at &lt;a href="https://github.com/open-wc/open-wc" rel="noopener noreferrer"&gt;open-wc&lt;/a&gt; and it served us well so far.&lt;/p&gt;

&lt;p&gt;Congratulations you now have type safety without a build step 🎉&lt;/p&gt;

&lt;p&gt;Feel free to also check out &lt;a href="https://github.com/daKmoR/generate-typescript-definition-files-from-javascript" rel="noopener noreferrer"&gt;the repository for this post&lt;/a&gt; and execute &lt;code&gt;npm run build:types&lt;/code&gt; or &lt;code&gt;npm run lint:types&lt;/code&gt; to see the magic live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;To sum it all up - why are we fans of TypeScript even though it requires a build step?&lt;/p&gt;

&lt;p&gt;It comes down to 2 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typings can be immensely useful (type safety, auto-complete, documentation, etc.) for you and/or your users&lt;/li&gt;
&lt;li&gt;TypeScript is very flexible and supports types for "just" JavaScript as well&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further Resources
&lt;/h2&gt;

&lt;p&gt;If you'd like to know more about using JSDoc for type safety, we recommend the following blog posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/dakmor/type-safe-web-components-with-jsdoc-4icf"&gt;Type-Safe Web Components with JSDoc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@trukrs/type-safe-javascript-with-jsdoc-7a2a63209b76" rel="noopener noreferrer"&gt;Type Safe JavaScript with JSDoc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/openwc" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or follow me on my personal &lt;a href="https://twitter.com/dakmor" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;br&gt;
Make sure to check out our other tools and recommendations at &lt;a href="https://open-wc.org" rel="noopener noreferrer"&gt;open-wc.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://dev.to/bennypowers"&gt;Benny&lt;/a&gt;, &lt;a href="https://github.com/LarsDenBakker" rel="noopener noreferrer"&gt;Lars&lt;/a&gt; and &lt;a href="https://twitter.com/passle_" rel="noopener noreferrer"&gt;Pascal&lt;/a&gt; for feedback and helping turn my scribbles to a followable story.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>jsdoc</category>
      <category>buildless</category>
    </item>
    <item>
      <title>Nested Dependencies in Frontend</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Tue, 02 Jul 2019 16:08:49 +0000</pubDate>
      <link>https://dev.to/open-wc/nested-dependencies-in-frontend-558c</link>
      <guid>https://dev.to/open-wc/nested-dependencies-in-frontend-558c</guid>
      <description>&lt;p&gt;So you got this awesome idea and now you want to actually do it. I'm pretty sure you do not want to start from scratch, so let's use existing open source packages.&lt;/p&gt;

&lt;p&gt;If you want to play along, all the code is on &lt;a href="https://github.com/daKmoR/nested-dependencies-in-frontend"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For our example case, we wanna use lit-element and lit-html.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;nested-dependecies-in-frontend
&lt;span class="nb"&gt;cd &lt;/span&gt;nested-dependecies-in-frontend
npm &lt;span class="nb"&gt;install &lt;/span&gt;lit-element lit-html@1.0.0 &lt;span class="nt"&gt;--save-exact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: we are using pinned versions on purpose here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then we just load both packages in our &lt;code&gt;main.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit-html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In order to find out how big our app will be, we would like to create a rollup bundle. First, install Rollup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; rollup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then create a &lt;code&gt;rollup.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bundle.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iife&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, add &lt;code&gt;"build": "rollup -c rollup.config.js &amp;amp;&amp;amp; du -h bundle.js"&lt;/code&gt; to our package.json's &lt;code&gt;scripts&lt;/code&gt; block, so we can easily build the file and output it's file size.&lt;br&gt;
Lets run it via &lt;code&gt;npm run build&lt;/code&gt; :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(!) Unresolved dependencies
https://rollupjs.org/guide/en#warning-treating-module-as-external-dependency
lit-element (imported by main.js)
lit-html (imported by main.js)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Oh! It doesn't work! 😭&lt;/p&gt;

&lt;p&gt;OK, I've heard this one before... We need to add some plugins so that Rollup will understand the way node resolution (i.e. bare module specifiers like &lt;code&gt;import { html } from 'lit-html'&lt;/code&gt;) works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; rollup-plugin-node-resolve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ import resolve from "rollup-plugin-node-resolve";
+
&lt;/span&gt;   export default {
    input: "main.js",
    output: {
      file: "bundle.js",
      format: "iife"
    },
&lt;span class="gi"&gt;+  plugins: [resolve()]
&lt;/span&gt;  };
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run build
&lt;span class="c"&gt;# ...&lt;/span&gt;
created bundle.js &lt;span class="k"&gt;in &lt;/span&gt;414ms
96K     bundle.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So that seems to work fine. 💪&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happens if Someone Prefers yarn?
&lt;/h3&gt;

&lt;p&gt;Doing a yarn install and then a build should result in the same output, right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn build
&lt;span class="c"&gt;# ...&lt;/span&gt;
created bundle.js &lt;span class="k"&gt;in &lt;/span&gt;583ms
124K    bundle.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Wow! That is unexpected - 124K for the &lt;code&gt;yarn&lt;/code&gt; build vs. 96K for &lt;code&gt;npm&lt;/code&gt;?&lt;br&gt;
It seems the yarn build contains some extra files... maybe a package was duplicated?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn list &lt;span class="nt"&gt;--pattern&lt;/span&gt; lit-&lt;span class="k"&gt;*&lt;/span&gt;
├─ lit-element@2.2.0
│  └─ lit-html@1.1.0
└─ lit-html@1.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Yup, both &lt;code&gt;lit-html&lt;/code&gt; versions &lt;code&gt;1.0.0&lt;/code&gt; and &lt;code&gt;1.1.0&lt;/code&gt; are installed.&lt;br&gt;
The reason is most likely that we pinned &lt;code&gt;lit-html&lt;/code&gt; to version &lt;code&gt;1.0.0&lt;/code&gt; in our root dependency when we installed it with the &lt;code&gt;npm install --save-exact lit-html@1.0.0&lt;/code&gt; command, above.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;npm&lt;/code&gt; seems to dedupe it fine, I don't feel safe using &lt;code&gt;npm&lt;/code&gt; because if the dependency tree becomes bigger npm also likes to install nested dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;ls &lt;/span&gt;lit-element lit-html
├─┬ lit-element@2.2.0
│ └── lit-html@1.0.0  deduped
└── lit-html@1.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also specially when you use some beta (e.g. &lt;code&gt;0.x.x&lt;/code&gt;) dependencies it becomes very tricky. As in this case &lt;a href="https://semver.org/#spec-item-4"&gt;SemVer&lt;/a&gt; says every &lt;code&gt;0.x.0&lt;/code&gt; release means a &lt;a href="https://semver.org/#how-should-i-deal-with-revisions-in-the-0yz-initial-development-phase"&gt;breaking change&lt;/a&gt;. This means &lt;code&gt;0.8.0&lt;/code&gt; is treated as incompatible with &lt;code&gt;0.9.0&lt;/code&gt;. Therefore even if the APIs you are using would work just fine with both versions you will always get nested dependencies which may break your application silently. e.g. there will be no warning or information on the terminal 😱 &lt;/p&gt;

&lt;h3&gt;
  
  
  How Node Resolution Works
&lt;/h3&gt;

&lt;p&gt;In nodejs, when you import a file using a bare specifier, e.g. &lt;code&gt;import { LitElement } from "lit-element";&lt;/code&gt; Node's module resolver function gets the string &lt;code&gt;lit-element&lt;/code&gt;, and begins searching all of the directories listed in &lt;code&gt;module.paths&lt;/code&gt; for the importing module, which you can inspect like any other value in the node REPL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node
module.paths
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'/some/path/nested-dependencies-in-frontend/node_modules'&lt;/span&gt;,
  &lt;span class="s1"&gt;'/some/path/node_modules'&lt;/span&gt;,
  &lt;span class="s1"&gt;'/some/node_modules'&lt;/span&gt;,
  &lt;span class="s1"&gt;'/node_modules'&lt;/span&gt;,
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;# unimportant folders are hidden here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Basically, node looks into every &lt;code&gt;node_modules&lt;/code&gt; folder, starting in the module's parent directory and moving up the file tree, until it finds a directory name which matches the module specifier (in our case, &lt;code&gt;lit-element&lt;/code&gt;). The resolution algorithm always starts at the current module's parent directory, so it's always relative to where you are importing the file from. If we would inspect &lt;code&gt;module.paths&lt;/code&gt; from within lit-element's directory, we'd see a different list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;node_modules/lit-element
&lt;span class="nv"&gt;$ &lt;/span&gt;node
module.paths
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'/some/path/nested-dependencies-in-frontend/node_modules/lit-element/node_modules'&lt;/span&gt;,
  &lt;span class="s1"&gt;'/some/path/nested-dependencies-in-frontend/node_modules'&lt;/span&gt;,
  &lt;span class="s1"&gt;'/some/path/node_modules'&lt;/span&gt;,
  &lt;span class="s1"&gt;'/some/node_modules'&lt;/span&gt;,
  &lt;span class="s1"&gt;'/node_modules'&lt;/span&gt;,
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can understand what node's nested dependencies are. Every module can have it's own &lt;code&gt;node_modules&lt;/code&gt; directory, &lt;em&gt;ad nauseum&lt;/em&gt;, and imports referenced in that module's files will always look in their closest &lt;code&gt;node_modules&lt;/code&gt; directory first...&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pros of Nested Dependencies on Node&lt;/th&gt;
&lt;th&gt;Cons of Nested Dependencies for Frontend&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Every package can have their own versions of every dependency&lt;/td&gt;
&lt;td&gt;Shipping the same code twice means longer download and processing times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packages are not influenced by dependencies of other packages in the application&lt;/td&gt;
&lt;td&gt;Stuff might break if the same code is imported twice from two different locations (e.g. performance optimizations via &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap"&gt;WeakMaps&lt;/a&gt; or singletons)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;There is no "high fee" to pay for accessing many extra files.&lt;/td&gt;
&lt;td&gt;Checking if a file exists is an extra request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On the server, you usually do not care too much about how much extra code (in files size) there is&lt;/td&gt;
&lt;td&gt;Overall, in short, your site will get slower&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Problems
&lt;/h3&gt;

&lt;p&gt;In short, automatic module resolution that prefers nesting may be dangerous for frontend.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We care about loading and parsing performance&lt;/li&gt;
&lt;li&gt;We care about file size&lt;/li&gt;
&lt;li&gt;Some packages must be singletons (i.e. unique in the module graph) to work properly in our application

&lt;ul&gt;
&lt;li&gt;Examples include &lt;code&gt;lit-html&lt;/code&gt; and &lt;code&gt;graphql&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;We should be in full control of what ends up on the client's browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Node-style module resolution, which was designed for a server-side environment, can turn these concerns into serious issues when adopted in the browser.&lt;br&gt;
IMHO, even if node resolution makes it technically possible, loading the code for a complex data-grid more than once should never be our goal as frontend developers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solutions
&lt;/h3&gt;

&lt;p&gt;Thankfully, there are solutions to these problems that we can use today, and proposals on the horizon which will altogether eliminate the need for such workarounds in the future.&lt;/p&gt;
&lt;h4&gt;
  
  
  Making it Work Today
&lt;/h4&gt;

&lt;p&gt;Here are some tips to work with bare module specifiers in your front end code today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure that the modules in your dependency tree all use similar version ranges of their common dependencies&lt;/li&gt;
&lt;li&gt;Avoid pinning specific package versions (like we did above with &lt;code&gt;npm i -S lit-html@1.0.0&lt;/code&gt;) wherever possible&lt;/li&gt;
&lt;li&gt;If you're using &lt;code&gt;npm&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;npm dedupe&lt;/code&gt; after installing packages to remove nested duplicates.&lt;/li&gt;
&lt;li&gt;You can try deleting your &lt;code&gt;package-lock.json&lt;/code&gt; and do a fresh install. Sometimes it magically helps 🧙‍♂️&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you're using &lt;code&gt;yarn&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Consider using &lt;a href="https://yarnpkg.com/lang/en/docs/selective-version-resolutions/"&gt;yarn resolutions&lt;/a&gt; to specify your preferred version of any duplicated packages&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  A Look Into the Future
&lt;/h4&gt;

&lt;p&gt;If we could tell the JavaScript environment (i.e. the browser) exactly at which &lt;code&gt;path&lt;/code&gt; to find the file specified by some string, we would have no need for node-style resolution or programming-time deduplication routines.&lt;br&gt;
We'd write something like this and pass it to the browser to specify which paths mapped to which packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lit-html"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/lit-html.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lit-element"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/lit-element.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using this import map to resolve package paths means there would always only be one version of &lt;code&gt;lit-html&lt;/code&gt; and &lt;code&gt;lit-element&lt;/code&gt;, because the global environment already knows exactly where to find them.&lt;/p&gt;

&lt;p&gt;Luckily ✨,  this is already a proposed spec called &lt;a href="https://github.com/WICG/import-maps"&gt;import maps&lt;/a&gt;. And since it's meant for the browser there's no need to do any transformation at all! You just provide the map and you don't need any build step while developing?&lt;/p&gt;

&lt;p&gt;Sounds crazy 😜? Let's try it out! 🤗&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Mind you this is an experimental API proposal, it hasn't been finalized or accepted by implementers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It currently only works in Chrome 75+, behind a flag.&lt;br&gt;
So enter &lt;code&gt;chrome://flags/&lt;/code&gt; in the URL bar and then search for &lt;code&gt;Built-in module infra and import maps&lt;/code&gt; and enable it.&lt;br&gt;
Here is a direct link to it: &lt;a&gt;chrome://flags/#enable-built-in-module-infra&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Using Import Maps in the Browser
&lt;/h4&gt;

&lt;p&gt;In order to use an import map, let's create an &lt;code&gt;index.html&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-GB"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"importmap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;imports&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit-html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./node_modules/lit-html/lit-html.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit-html/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./node_modules/lit-html/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./node_modules/lit-element/lit-element.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit-element/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./node_modules/lit-element/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My app&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;crowd-chant&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"what"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Bare Imports!&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"when"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Now!&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/crowd-chant&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and adjust the &lt;code&gt;main.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CrowdChant&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;h2&amp;gt;What do we want?&amp;lt;/h2&amp;gt;
      &amp;lt;slot name="what"&amp;gt;&amp;lt;/slot&amp;gt;
      &amp;lt;h2&amp;gt;When do we want them?&amp;lt;/h2&amp;gt;
      &amp;lt;time&amp;gt;&amp;lt;slot name="when"&amp;gt;Now!&amp;lt;/slot&amp;gt;&amp;lt;/time&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crowd-chant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CrowdChant&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Save the file then serve it locally by running &lt;code&gt;npx http-server -o&lt;/code&gt; in the same directory.&lt;br&gt;
This will open &lt;a href="http://localhost:8080/"&gt;http://localhost:8080/&lt;/a&gt; where you will see your custom element rendered on screen. 🎉&lt;/p&gt;

&lt;p&gt;What kind of black magic is this 🔮? Without any bundlers, tools, or build step, we wrote a componentized app with the kind of bare specifiers we've come to know and love.&lt;/p&gt;

&lt;p&gt;Lets break it down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit-html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// will actually import "./node_modules/lit-html/lit-html.js"&lt;/span&gt;
&lt;span class="c1"&gt;// because of&lt;/span&gt;
&lt;span class="c1"&gt;// "lit-html": "./node_modules/lit-html/lit-html.js",&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repeat&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit-html/directives/repeat.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;// will actually import "./node_modules/lit-html/directives/repeat.js"&lt;/span&gt;
&lt;span class="c1"&gt;// beacause of&lt;/span&gt;
&lt;span class="c1"&gt;// "lit-html/": "./node_modules/lit-html/",&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So this means&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can import packages directly since the package name is mapped to a specific file&lt;/li&gt;
&lt;li&gt;You can import subdirectories and files, since &lt;code&gt;packageName + '/'&lt;/code&gt; is mapped to its directory&lt;/li&gt;
&lt;li&gt;You must &lt;em&gt;not&lt;/em&gt; omit the &lt;code&gt;.js&lt;/code&gt; when importing a file from a subdirectory&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  What Does this All Mean for my Production Build?
&lt;/h4&gt;

&lt;p&gt;It's important to once again note that this is still experimental technology. In any event, you may still want to do an optimized build for production sites using tools like Rollup. We are exploring together what these new APIs will do for our websites and apps. The underlying &lt;code&gt;import-maps&lt;/code&gt; proposal is still unstable, but that shouldn't stop us from experimenting and extracting utility from it. After all, most of us are comfortable using &lt;code&gt;babel&lt;/code&gt; to enable experimental syntax like decorators, even though that proposal has at time of this writing at least four flavours.&lt;/p&gt;

&lt;p&gt;If you want to try import maps today even in unsupported browsers, you'll need either a build step or a runtime solution like systemjs. For the build-step option, you'll replace the &lt;code&gt;rollup-plugin-node-resolve&lt;/code&gt; with something that respects your &lt;code&gt;import map&lt;/code&gt; instead of using node resolution.&lt;/p&gt;

&lt;p&gt;And wouldn't it be really nice if you could just point rollup to your &lt;code&gt;index.html&lt;/code&gt; and have it figure out what your entry points are and if there is an import map?&lt;/p&gt;

&lt;p&gt;That's why at &lt;a href="https://open-wc.org"&gt;open-wc&lt;/a&gt; we're releasing experimental support for import maps with our &lt;code&gt;rollup-plugin-index-html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And you can read all about it here on dev.to. Watch this space for the announcement 😉.&lt;/p&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/openwc"&gt;Twitter&lt;/a&gt;, or follow me on my personal &lt;a href="https://twitter.com/dakmor"&gt;Twitter&lt;/a&gt;.&lt;br&gt;
Make sure to check out our other tools and recommendations at &lt;a href="https://open-wc.org"&gt;open-wc.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://dev.to/bennypowers"&gt;Benny&lt;/a&gt; and &lt;a href="https://github.com/LarsDenBakker"&gt;Lars&lt;/a&gt; for feedback and helping turn my scribbles to a followable story.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>importmaps</category>
      <category>node</category>
      <category>rollup</category>
    </item>
    <item>
      <title>Testing Workflow for Web Components</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Tue, 09 Apr 2019 17:55:38 +0000</pubDate>
      <link>https://dev.to/open-wc/testing-workflow-for-web-components-g73</link>
      <guid>https://dev.to/open-wc/testing-workflow-for-web-components-g73</guid>
      <description>&lt;p&gt;Whenever you ship something to be used by others, you take on a responsibility to deliver safe and stable code. One way to address this is by testing your code.&lt;/p&gt;

&lt;p&gt;No matter how small - no matter how simple your project, there should always ideally be tests.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes, I know reality hits hard and there will be many cases where that doesn't happen - but you should always strive to have tests&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;In this tutorial, we're going to make a simple version of an input element. By the end of it, you'll gain the skills and knowledge to put open-wc testing tools to practice; and build a solid, accessible, and well-tested input component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Warning
&lt;/h3&gt;

&lt;p&gt;This is an in-depth tutorial showing a few pitfalls and tough cases when working with web components. This is definitely for more advanced users. You should have a basic knowledge about &lt;a href="https://lit-element.polymer-project.org/"&gt;LitElement&lt;/a&gt; and &lt;a href="https://dev.to/dakmor/type-safe-web-components-with-jsdoc-4icf"&gt;JSDoc Types&lt;/a&gt;. Having an idea what &lt;a href="https://mochajs.org/"&gt;Mocha&lt;/a&gt;, &lt;a href="https://www.chaijs.com/api/bdd/"&gt;Chai BDD&lt;/a&gt;, &lt;a href="https://karma-runner.github.io/latest/index.html"&gt;Karma&lt;/a&gt; is might help a little as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We are thinking about posting an easier digestible version of this so if that is something you would like to see - let us know in the comments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to play along - all the code is on &lt;a href="https://github.com/daKmoR/testing-workflow-for-web-components"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Get Started!
&lt;/h2&gt;

&lt;p&gt;Run in your console&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm init @open-wc

&lt;span class="c"&gt;# Results in this flow&lt;/span&gt;
✔ What would you like to &lt;span class="k"&gt;do &lt;/span&gt;today? › Scaffold a new project
✔ What would you like to scaffold? › Web Component
&lt;span class="c"&gt;# Select with space! "Testing" =&amp;gt; just enter will move one with no selection&lt;/span&gt;
✔ What would you like to add? › Testing
✔ Would you like to scaffold examples files &lt;span class="k"&gt;for&lt;/span&gt;? › Testing
✔ What is the tag name of your application/web component? … a11y-input
✔ Do you want to write this file structure to disk? › Yes
Writing..... &lt;span class="k"&gt;done&lt;/span&gt;
✔ Do you want to &lt;span class="nb"&gt;install &lt;/span&gt;dependencies? › No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details please see &lt;a href="https://open-wc.org/testing/"&gt;https://open-wc.org/testing/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Delete &lt;code&gt;src/A11yInput.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Modify &lt;code&gt;src/a11y-input.js&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A11yInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a11y-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;A11yInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;test/a11y-input.test.js&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* eslint-disable no-unused-expressions */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@open-wc/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/a11y-input.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @typedef {import('../src/a11y-input.js').A11yInput} A11yInput
 */&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a11y input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has by default an empty string as label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our tests so far consist of a single feature (the &lt;code&gt;label&lt;/code&gt; property) and a single assertion (&lt;code&gt;expect&lt;/code&gt;). We're using karma and chai's BDD syntax, so we group sets of tests (&lt;code&gt;it&lt;/code&gt;) under the features or APIs they relate to (&lt;code&gt;describe&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let's see if everything works correctly by running: &lt;code&gt;npm run test&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUMMARY:
✔ 0 tests completed
✖ 1 test failed

FAILED TESTS:
  a11y input
    ✖ has by default an empty string as label
      HeadlessChrome 73.0.3683 (Windows 10.0.0)
    AssertionError: expected undefined to equal ''

      + expected - actual

      -[undefined]
      +""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome - just as expected (🥁), we have a failing test :)&lt;/p&gt;

&lt;p&gt;Let's switch into watch mode, which will run the tests continuously whenever you make changes to your code.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run test:watch&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cue3szkhaqk8jvm8cab.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cue3szkhaqk8jvm8cab.gif" alt="01-watch-mode-intro" width="1321" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following code was added in the video above to &lt;code&gt;src/a11y-input.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, so good? Still with us? Great! Let's up the game a little...&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a Test for Shadow DOM
&lt;/h3&gt;

&lt;p&gt;Let's add an assertion to test the contents of our element's shadow root.&lt;/p&gt;

&lt;p&gt;If we want to be sure that our element behaves/looks the same we should make sure its dom structure remains the same.&lt;br&gt;
So let's compare the actual shadow dom to what we want it to be.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has a static shadowDom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✖ has a static shadowDom
AssertionError: expected '' to equal '\n      &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;\n      &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;\n    '

  + expected - actual

  +
  +      &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;
  +      &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;
  +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So let's implement that in our element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interesting, the test should be green... but it's not 🤔 Let's take a look.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✖ has a static shadowDom
AssertionError: expected '&amp;lt;!----&amp;gt;\n      &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;\n      &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;\n    &amp;lt;!----&amp;gt;' to equal '\n        &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;\n        &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;\n    '

  + expected - actual

  -&amp;lt;!----&amp;gt;
  -      &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;
  -      &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;
  -    &amp;lt;!----&amp;gt;
  +
  +        &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;
  +        &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;
  +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have noticed those weird empty comment &lt;code&gt;&amp;lt;!----&amp;gt;&lt;/code&gt; tags. They are markers that &lt;code&gt;lit-html&lt;/code&gt; uses to remember where dynamic parts are, so it can be updated efficiently. For testing, however, this can be a little annoying to deal with.&lt;/p&gt;

&lt;p&gt;If we use &lt;code&gt;innerHTML&lt;/code&gt; to compare the DOM, we'd have to rely on simple string equality. Under those circumstances, we'd have to exactly match the generated DOM's whitespace, comments, etc; in other words: it will need to be a perfect match. Really all we need to test is that the elements we want to render are rendered. We want to test the &lt;em&gt;semantic&lt;/em&gt; contents of the shadow root.&lt;/p&gt;

&lt;p&gt;Fortunately, we've got you covered. If you're using &lt;code&gt;@open-wc/testing&lt;/code&gt; then it automatically loads the &lt;code&gt;@open-wc/semantic-dom-diff&lt;/code&gt; chai plugin for us to use.&lt;/p&gt;

&lt;p&gt;So let's try it out 💪&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// old:&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// new:&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bam 🎉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a11y input
  ✔ has by default an empty string as a label
  ✔ has a static shadowDom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How does shadowDom.to.equal() work?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;It gets the &lt;code&gt;innerHTML&lt;/code&gt; of the shadow root&lt;/li&gt;
&lt;li&gt;Parses it (actually, the browser parses it - no library needed)&lt;/li&gt;
&lt;li&gt;Normalizes it (potentially every tag/property on its own line)&lt;/li&gt;
&lt;li&gt;Parses and normalizes the expected HTML string&lt;/li&gt;
&lt;li&gt;Passes both normalized DOM strings on to chai's default compare function&lt;/li&gt;
&lt;li&gt;In case of failure, groups, and displays any differences in a clear manner&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to know more, please check out the documentation of &lt;a href="https://open-wc.org/testing/semantic-dom-diff.html"&gt;semantic-dom-diff&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing the "Light" DOM
&lt;/h3&gt;

&lt;p&gt;We can do exactly the same thing with the light DOM. (The DOM which will be provided by our user or our defaults, i.e. the element's &lt;code&gt;children&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has 1 input and 1 label in light-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input .label=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;lightDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;label slot="label"&amp;gt;foo&amp;lt;/label&amp;gt;
    &amp;lt;input slot="input"&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let's implement it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelEl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we tested our light and shadow dom 💪 and our tests run clean 🎉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Using the DOM API in a lit-element's lifecycle is an anti-pattern, however to allow for a11y it might be a real use case - anyways it's great for illustration purposes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using Our Element in an App
&lt;/h3&gt;

&lt;p&gt;So now that we have a basic a11y input let's use it - and test it - in our application.&lt;/p&gt;

&lt;p&gt;Again we start with a skeleton &lt;code&gt;src/my-app.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* eslint-disable class-methods-use-this */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our test in &lt;code&gt;test/my-app.test.js&lt;/code&gt;;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* eslint-disable no-unused-expressions */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@open-wc/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/my-app.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @typedef {import('../src/my-app.js').MyApp} MyApp
 */&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Filter App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has a heading and a search field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {MyApp} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;my-app .label=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/my-app&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;h1&amp;gt;My Filter App&amp;lt;/h1&amp;gt;
      &amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the test =&amp;gt; fails and then we add the implementation to &lt;code&gt;src/a11y-input.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;h1&amp;gt;My Filter App&amp;lt;/h1&amp;gt;
    &amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But oh no! That should be green now...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUMMARY:
✔ 3 tests completed
✖ 1 test failed

FAILED TESTS:
  My Filter App
    ✖ has a heading and a search field
    AssertionError: expected '&amp;lt;h1&amp;gt;\n  My Filter App\n&amp;lt;/h1&amp;gt;\n&amp;lt;a11y-input&amp;gt;\n  &amp;lt;label slot="label"&amp;gt;\n  &amp;lt;/label&amp;gt;\n  &amp;lt;input slot="input"&amp;gt;\n&amp;lt;/a11y-input&amp;gt;\n' to equal '&amp;lt;h1&amp;gt;\n  My Filter App\n&amp;lt;/h1&amp;gt;\n&amp;lt;a11y-input&amp;gt;\n&amp;lt;/a11y-input&amp;gt;\n'

      + expected - actual

       &amp;lt;h1&amp;gt;
         My Filter App
       &amp;lt;/h1&amp;gt;
       &amp;lt;a11y-input&amp;gt;
      -  &amp;lt;label slot="label"&amp;gt;
      -  &amp;lt;/label&amp;gt;
      -  &amp;lt;input slot="input"&amp;gt;
       &amp;lt;/a11y-input&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is happening?&lt;br&gt;
Do you remember that we had a specific test to ensure the light-dom of a11y-input?&lt;br&gt;
So even if the users only puts &lt;code&gt;&amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;&lt;/code&gt; in his code - what actually comes out is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a11y-input&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/a11y-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;e.g. &lt;code&gt;a11y-input&lt;/code&gt; is actually creating nodes inside of your &lt;code&gt;my-app&lt;/code&gt; shadow dom. Preposterous! For our example here we say that's what we want.&lt;br&gt;
So how can we still test it?&lt;/p&gt;

&lt;p&gt;Luckily &lt;code&gt;.shadowDom&lt;/code&gt; has another ace up its sleeve; it allows us to ignore parts of dom.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;h1&amp;gt;My Filter App&amp;lt;/h1&amp;gt;
  &amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ignoreChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a11y-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even specify the following properties as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ignoreChildren&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ignoreTags&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ignoreAttributes&lt;/code&gt; (globally or for specific tags)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details please see &lt;a href="https://open-wc.org/testing/semantic-dom-diff.html"&gt;semantic-dom-diff&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Snapshot testing
&lt;/h4&gt;

&lt;p&gt;If you have a lot of big dom trees writing/maintaining all those manually written expects is going to be tough.&lt;br&gt;
To help you with that there are semi/automatic snapshots.&lt;/p&gt;

&lt;p&gt;So if we change our code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// from&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;slot name="label"&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;slot name="input"&amp;gt;&amp;lt;/slot&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// to&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equalSnapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we now execute &lt;code&gt;npm run test&lt;/code&gt; it will create a file &lt;code&gt;__snapshots__/a11y input.md&lt;/code&gt; and fill it with something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# `a11y input`

#### `has a static shadowDom`

``html
&amp;lt;slot name="label"&amp;gt;
&amp;lt;/slot&amp;gt;
&amp;lt;slot name="input"&amp;gt;
&amp;lt;/slot&amp;gt;

``
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we wrote before by hand can now be auto-generated on init or forcefully via &lt;code&gt;npm run test:update-snapshots&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the file &lt;code&gt;__snapshots__/a11y input.md&lt;/code&gt; already exists it will compare it with the output and you will get errors if your html output changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FAILED TESTS:
  a11y input
    ✖ has a static shadowDom
      HeadlessChrome 73.0.3683 (Windows 10.0.0)
    AssertionError: Received value does not match stored snapshot 0

      + expected - actual

      -&amp;lt;slot name="label-wrong"&amp;gt;
      +&amp;lt;slot name="label"&amp;gt;
       &amp;lt;/slot&amp;gt;
       &amp;lt;slot name="input"&amp;gt;
      -&amp;lt;/slot&amp;gt;
      +&amp;lt;/slot&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details please see &lt;a href="https://open-wc.org/testing/semantic-dom-diff.html"&gt;semantic-dom-diff&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think that's now enough about comparing dom trees...&lt;br&gt;
It's time for a change 🤗&lt;/p&gt;
&lt;h3&gt;
  
  
  Code coverage
&lt;/h3&gt;

&lt;p&gt;Another useful metric we get when testing with the open-wc setup is code coverage.&lt;br&gt;
So what does it mean and how can we get it? &lt;a href="https://www.wikiwand.com/en/Code_coverage"&gt;Code coverage&lt;/a&gt; is a measure of &lt;em&gt;how much&lt;/em&gt; of our code is checked by tests. If there's a line, statement, function, or branch (e.g. &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; statement) that our tests don't cover our coverage score will be affected.&lt;br&gt;
A simple &lt;code&gt;npm run test&lt;/code&gt; is all we need and you will get the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=============================== Coverage summary ===============================
Statements   : 100% ( 15/15 )
Branches     : 100% ( 0/0 )
Functions    : 100% ( 5/5 )
Lines        : 100% ( 15/15 )
================================================================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which means that 100% of our code's statements, branches, functions, and lines are covered by tests. Pretty neat!&lt;/p&gt;

&lt;p&gt;So let's go the other way and add code to &lt;code&gt;src/a11y-input.js&lt;/code&gt; before adding a test. Let's say we want to access the value of our input directly via our custom element and whenever its value is 'cat' we want to log something.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a vastly different result&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUMMARY:
✔ 4 tests completed
TOTAL: 4 SUCCESS

=============================== Coverage summary ===============================
Statements   : 81.82% ( 18/22 )
Branches     : 0% ( 0/2 )
Functions    : 75% ( 6/8 )
Lines        : 81.82% ( 18/22 )
================================================================================
06 04 2019 10:40:45.380:ERROR [reporter.coverage-istanbul]: Coverage for statements (81.82%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for lines (81.82%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for branches (0%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for functions (75%) does not meet global threshold (90%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our coverage is way lower than before. Our test command even fails, even though all the tests run successfully.&lt;br&gt;
This is because by default open-wc's config sets a 90% threshold for code coverage.&lt;/p&gt;

&lt;p&gt;If we want to improve coverage we need to add tests - so let's do it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;can set/get the input value directly via the custom element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input .value=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;uh oh 😱 we wanted to improve coverage but now we need fix an actual bug first 😞&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FAILED TESTS:
  a11y input
    ✖ can set/get the input value directly via the custom element
    TypeError: Cannot set property 'value' of null        at HTMLElement.set value [as value]
    // ... =&amp;gt; long error stack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was unexpected... on first glance, I don't really know what that means... better to check some actual nodes and inspect them in the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging in the Browser
&lt;/h3&gt;

&lt;p&gt;When we run our test with watch, karma sets up a persistent browser environment to run tests in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be sure you started with &lt;code&gt;npm run test:watch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;visit &lt;a href="http://localhost:9876/debug.html"&gt;http://localhost:9876/debug.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should see something like this&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvb10f0p8nw5vo6mvvkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvb10f0p8nw5vo6mvvkg.png" alt="02-debugging-in-browser" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can click on the circled play button to only run one individual test.&lt;/p&gt;

&lt;p&gt;So let's open the Chrome Dev Tools (F12) and put a debugger in the test code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;can set/get the input value directly via the custom element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input .value=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dang.. the error happens even before that point...&lt;br&gt;
"Fatal" errors like this are a little tougher as they are not failing tests but sort of a complete meltdown of your full component.&lt;/p&gt;

&lt;p&gt;Ok, let's put some code in the &lt;code&gt;setter&lt;/code&gt; directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;debugger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, that worked so our chrome console we write &lt;code&gt;console.log(this)&lt;/code&gt; let's see what we have here&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a11y&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;root &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a11y-input&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahh there we have it - the shadow dom is not yet rendered when the setter is called.&lt;br&gt;
So let's be safe and add a check before&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fatel error is gone 🎉&lt;br&gt;
But we now have a failing test 😭&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✖ can set/get the input value directly via the custom element
AssertionError: expected '' to equal 'foo'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We may need a change of tactic 🤔&lt;br&gt;
We can add it as a separate &lt;code&gt;value&lt;/code&gt; property and sync when needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we're finally back in business! 🎉&lt;/p&gt;

&lt;p&gt;ok bug fixed - can we please get back to coverage? thank you 🙏&lt;/p&gt;

&lt;h3&gt;
  
  
  Back to coverage
&lt;/h3&gt;

&lt;p&gt;With this added test we made some progress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=============================== Coverage summary ===============================
Statements   : 95.83% ( 23/24 )
Branches     : 50% ( 2/4 )
Functions    : 100% ( 7/7 )
Lines        : 95.83% ( 23/24 )
================================================================================
06 04 2019 13:18:54.902:ERROR [reporter.coverage-istanbul]: Coverage for branches (50%) does not meet global threshold (90%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However we are still not fully there - the question is why?&lt;/p&gt;

&lt;p&gt;To find out open &lt;code&gt;coverage/index.html&lt;/code&gt; in your browser. No web server needed just open the file in your browser - on a mac you can do that from the command line with &lt;code&gt;open coverage/index.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will see something like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xycmlq5gwwwdwqe5yho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xycmlq5gwwwdwqe5yho.png" alt="03-coverage-overview" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you click on &lt;code&gt;a11y-input.js&lt;/code&gt; you get a line by line info how often they got executed.&lt;br&gt;
So we can immediately see which lines are not executed yet by our tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkja3i4bgxnor7wkqo3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkja3i4bgxnor7wkqo3i.png" alt="04-coverage-line-by-line" width="553" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let's add a test for that&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logs "We like cats too :)" if the value is "cat"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input .value=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// somehow check that console.log was called&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=============================== Coverage summary ===============================
Statements   : 100% ( 24/24 )
Branches     : 75% ( 3/4 )
Functions    : 100% ( 7/7 )
Lines        : 100% ( 24/24 )
================================================================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, we are back at 100% on statements but we still have something missing on branches.&lt;br&gt;
Let's see why?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4syjz96bkowskq86xcv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4syjz96bkowskq86xcv.png" alt="05-coverage-line-by-line-else" width="480" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;E&lt;/code&gt; means &lt;code&gt;else path not taken&lt;/code&gt;.&lt;br&gt;
So whenever the function &lt;code&gt;update&lt;/code&gt; gets called there is always a property &lt;code&gt;value&lt;/code&gt; in the changedProperties.&lt;/p&gt;

&lt;p&gt;We have &lt;code&gt;label&lt;/code&gt; as well so it's a good idea to test it. 👍&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;can update its label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;a11y-input label="foo"&amp;gt;&amp;lt;/a11y-input&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;boom 100% 💪 we win 🥇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=============================== Coverage summary ===============================
Statements   : 100% ( 24/24 )
Branches     : 100% ( 4/4 )
Functions    : 100% ( 7/7 )
Lines        : 100% ( 24/24 )
================================================================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait we didn't even finish the test above - the code is still&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// somehow check that console.log was called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How come we have 100% test coverage?
&lt;/h4&gt;

&lt;p&gt;Lets first try to understand how code coverage works 🤔&lt;br&gt;
The way code coverage gets measured is by applying a form of &lt;code&gt;instrumentation&lt;/code&gt;. In short, before our code is executed it gets changed (&lt;code&gt;instrumented&lt;/code&gt;) and it behaves something like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is a super simplified version for illustration purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// becomes something like this (psoido code)&lt;/span&gt;
&lt;span class="nx"&gt;__instrumented&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;functionUpdate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;__instrumented&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;functionUpdateBranch1yes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;__instrumented&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;functionUpdateBranch1no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, your code gets littered with many many flags. Based on which flags get trigger a statistic gets created.&lt;/p&gt;

&lt;p&gt;So 100% test coverage only means that every line you have in your code was executed at least once after all your tests finished. It does &lt;em&gt;not&lt;/em&gt; mean that you tested everything, or if your tests make the correct assertions.&lt;/p&gt;

&lt;p&gt;So even though we already have 100% code coverage we are still going to improve our log test.&lt;/p&gt;

&lt;p&gt;You should, therefore, see code coverage as a tool that only gives you guidance and help on spotting some missing tests, rather than a hard guarantee of code quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spying on Code
&lt;/h3&gt;

&lt;p&gt;If you want to check how often or with which parameters a function gets called, that's called spying.&lt;br&gt;
open-wc recommends the venerable &lt;a href="https://sinonjs.org/"&gt;sinon&lt;/a&gt; package, which provides many tools for spying and other related tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; sinon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you create a spy on a specific object and then you can check how often it gets called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sinon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;outputs "We like cats too :)" if the value is set to "cat"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callCount&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uh oh... the test fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AssertionError: expected 0 to equal 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Messing with global objects like &lt;code&gt;console&lt;/code&gt; might have &lt;a href="https://gyandeeps.com/console-stubbing/"&gt;side effects&lt;/a&gt; so let's better refactor using a dedicated log function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This result in no global object in our test code - sweet 🤗&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logs "We like cats too :)" if the value is set to "cat"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callCount&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, we still get the same error. Let's debug... boohoo apparently &lt;code&gt;update&lt;/code&gt; is not sync - a wrong assumption I made 🙈 I am saying &lt;em&gt;assumptions are dangerous&lt;/em&gt; quite often - still I fall for it from time to time 😢.&lt;/p&gt;

&lt;p&gt;So what can we do? Sadly there seems to be no public api to do some sync actions triggered by an property update.&lt;br&gt;
Let's create an issue for it &lt;a href="https://github.com/Polymer/lit-element/issues/643"&gt;https://github.com/Polymer/lit-element/issues/643&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For now apparently, the only way is to rely on a &lt;em&gt;private&lt;/em&gt; api. 🙈&lt;br&gt;
Also, we needed to move the value sync to &lt;code&gt;updated&lt;/code&gt; so it gets executed after every dom render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;_requestUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_requestUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is the updated test for the logging&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logs "We like cats too :)" if the value is set to "cat"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {A11yInput} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;a11y-input&amp;gt;&amp;lt;/a11y-input&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callCount&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We like cats too :)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// different values do NOT log&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callCount&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callCount&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;wow, that was a little tougher than expected but we did it 💪&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUMMARY:
✔ 7 tests completed
TOTAL: 7 SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running Tests Without Karma Framework
&lt;/h3&gt;

&lt;p&gt;The Karma framework is powerful and feature-rich, but sometimes we might want to pare-down our testing regiment. The nice thing with everything we proposed so far is that we only used browser-standard es modules with no need for transpilation, &lt;a href="https://open-wc.org/about/rationales.html#workflows"&gt;with the one exception of bare modules specifiers&lt;/a&gt;.&lt;br&gt;
So just by creating a &lt;code&gt;test/index.html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"../node_modules/mocha/mocha.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"../node_modules/mocha/mocha.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"mocha"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;mocha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bdd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./a11y-input.test.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./my-app.test.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;mocha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkLeaks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;mocha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and opening it via &lt;code&gt;owc-dev-server&lt;/code&gt; in chrome, it will work perfectly fine.&lt;br&gt;
We got everything up and running without &lt;code&gt;webpack&lt;/code&gt; or &lt;code&gt;karma&lt;/code&gt; - sweet 🤗&lt;/p&gt;
&lt;h3&gt;
  
  
  Do the Cross-Browser Thing
&lt;/h3&gt;

&lt;p&gt;We now feel pretty comfortable with our web component. It's tested and covered; there's just one more step - we want to make sure it runs and is tested in all browsers.&lt;/p&gt;

&lt;p&gt;Open WC recommends &lt;a href="https://www.browserstack.com/"&gt;Browserstack&lt;/a&gt; for cross-browser testing. If you haven't set it up yet, you can do it now - here is the link again - &lt;a href="https://open-wc.org/testing/"&gt;https://open-wc.org/testing/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let's just run it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test:bs

SUMMARY:
✔ 42 tests completed
TOTAL: 42 SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, that works nicely! 🤗&lt;/p&gt;

&lt;p&gt;If there are failing tests it will output them in the summary with the specific browser where it failed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUMMARY:
✔ 40 tests completed
✖ 2 tests failed

FAILED TESTS:
  a11y input
    ✖ has a static shadowDom
      Firefox 64.0.0 (Windows 10.0.0)
      Safari 12.0.0 (Mac OS X 10.14.0)
    expected '&amp;lt;slot name="label"&amp;gt;\n&amp;lt;/slot&amp;gt;\n&amp;lt;slot name="input"&amp;gt;\n&amp;lt;/slot&amp;gt;\n&amp;lt;style&amp;gt;\n&amp;lt;/style&amp;gt;\n' to equal '&amp;lt;slot name="label"&amp;gt;\n&amp;lt;/slot&amp;gt;\n&amp;lt;slot name="input"&amp;gt;\n&amp;lt;/slot&amp;gt;\n'

      + expected - actual

       &amp;lt;slot name="label"&amp;gt;
       &amp;lt;/slot&amp;gt;
       &amp;lt;slot name="input"&amp;gt;
       &amp;lt;/slot&amp;gt;
      -&amp;lt;style&amp;gt;
      -&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to debug a particular browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;npm run test:legacy:watch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;visit &lt;a href="http://localhost:9876/debug.html"&gt;http://localhost:9876/debug.html&lt;/a&gt; with that browser (either it locally or via browserstack)&lt;/li&gt;
&lt;li&gt;select a specific test (or use &lt;code&gt;it.only()&lt;/code&gt; in code)&lt;/li&gt;
&lt;li&gt;start debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also if you want to adjust the browser that gets tested you can adjust your &lt;code&gt;karma.bs.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if you want to add the &lt;code&gt;Firefox ESR&lt;/code&gt; to your list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bsSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;createBaseConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;browserStack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testing-workflow-for-web-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;browsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bs_win10_firefox_ESR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="c1"&gt;// define browsers&lt;/span&gt;
      &lt;span class="c1"&gt;// https://www.browserstack.com/automate/capabilities&lt;/span&gt;
      &lt;span class="na"&gt;customLaunchers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;bs_win10_firefox_ESR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BrowserStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Firefox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;browser_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;60&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Windows&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;os_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or maybe you want to test only 2 specific browsers?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;browsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;replace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nf"&gt;bsSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;createBaseConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;browserStack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testing-workflow-for-web-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;browsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bs_win10_ie_11&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bs_win10_firefox_ESR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This uses the &lt;a href="https://github.com/survivejs/webpack-merge#merging-with-strategies"&gt;webpack merge strategies&lt;/a&gt; replace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Testing is important for every project. Be sure to write as many as you can.&lt;/li&gt;
&lt;li&gt;Try to keep your code coverage high, but remember it's not a magic guarantee, so it doesn't always need to be 100%.&lt;/li&gt;
&lt;li&gt;Debug in the browser via &lt;code&gt;npm run test:watch&lt;/code&gt;. For legacy browsers, use &lt;code&gt;npm run test:legacy.watch&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run the tests in your CI (works perfectly well together with browserstack). See our recommendations at &lt;a href="https://open-wc.org/automating/"&gt;automating&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/openwc"&gt;Twitter&lt;/a&gt;, or follow me on my personal &lt;a href="https://twitter.com/dakmor"&gt;Twitter&lt;/a&gt;.&lt;br&gt;
Make sure to check out our other tools and recommendations at &lt;a href="https://open-wc.org"&gt;open-wc.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://dev.to/thepassle"&gt;Pascal&lt;/a&gt; and &lt;a href="https://dev.to/bennypowers"&gt;Benny&lt;/a&gt; for feedback and helping turn my scribbles to a followable story.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>javascript</category>
      <category>testing</category>
      <category>karma</category>
    </item>
    <item>
      <title>Type-Safe Web Components with JSDoc</title>
      <dc:creator>Thomas Allmer</dc:creator>
      <pubDate>Thu, 28 Mar 2019 10:03:07 +0000</pubDate>
      <link>https://dev.to/dakmor/type-safe-web-components-with-jsdoc-4icf</link>
      <guid>https://dev.to/dakmor/type-safe-web-components-with-jsdoc-4icf</guid>
      <description>&lt;p&gt;Writing code is tough and writing it in a way that makes sense to others (or your future self) is even tougher. That's why documentation is a very important part of every software project.&lt;/p&gt;

&lt;p&gt;I'm sure we've all found ourselves in the following situation: You're happily coding and just found a nice library that can help you, so you start using it...&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doTheThing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But, did &lt;code&gt;foo.doTheThing()&lt;/code&gt; take a string first and then the number or the other way around?&lt;/p&gt;

&lt;p&gt;So you head over to &lt;a href="http://foo-lib.org" rel="noopener noreferrer"&gt;http://foo-lib.org&lt;/a&gt; and about 5 clicks later you get to the function signature and find out how to use it. First of all, you're already lucky as not many libraries have good documentation 😱&lt;/p&gt;

&lt;p&gt;However it already painfully shows that the information is not as close to your workflow as it should be. You have to stop coding and search for the info while it could be directly in your editor. 😊&lt;/p&gt;

&lt;p&gt;So we can definitely do better 🤗 Let's get started with a very simple web component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: We will be assuming the editor in use is VS Code.&lt;/p&gt;

&lt;p&gt;If you wanna play along - all the code is on &lt;a href="https://github.com/daKmoR/type-safe-webcomponents-with-jsdoc" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &amp;lt;title-bar&amp;gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fne93j9na1hc9j9ewhq1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fne93j9na1hc9j9ewhq1j.png" alt="title-bar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;title-bar&amp;gt;&lt;/span&gt;
  #shadow-root (open)
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;You are awesome&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dot"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"left: 0px; top: 0px"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"I am dot"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/title-bar&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It's just a little box with a&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;title property&lt;/li&gt;
&lt;li&gt;darkMode property/attribute&lt;/li&gt;
&lt;li&gt;formatter function&lt;/li&gt;
&lt;li&gt;a sidebar property on the left&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will use LitElement to create it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: We use JavaScript here - but for the most part (except for the type casting &amp;amp; definitions) the example would be the same for TypeScript.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TitleBar&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark-mode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are awesome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;darkMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I am dot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// positioning the bar like this is just for illustration purposes =&amp;gt; do not do this&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/h1&amp;gt;
      &amp;lt;div
        class="dot"
        style=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="s2"&gt;`left: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px; top: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
        title=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;gt;&amp;lt;/div&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// we'll get to this later&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// we'll get to this later&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TitleBar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  What you get when you use it
&lt;/h3&gt;

&lt;p&gt;Let's query our newly created element. 😊&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here our editor can't know what &lt;code&gt;el&lt;/code&gt; actually is so there is no way it can help us in writing better code.&lt;br&gt;
That means no code completion for our own properties even though that information is available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25wrs2y4yfknnqn7bl3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25wrs2y4yfknnqn7bl3f.png" alt="autoCompleteMissing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what we need to do is cast it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {TitleBar} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we already get auto completion. 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5t8mkgz0lgvmcj5l03n2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5t8mkgz0lgvmcj5l03n2.png" alt="autoCompleteTypes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However we can still write code like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and nobody will complain.&lt;/p&gt;

&lt;p&gt;Let's change that 💪&lt;/p&gt;

&lt;h3&gt;
  
  
  Add type linting
&lt;/h3&gt;

&lt;p&gt;Add a &lt;code&gt;tsconfig.json&lt;/code&gt; file to your project&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"es2017"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checkJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitThis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alwaysStrict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/@open-wc/**/*.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/!(@open-wc)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That is all you need to get VS Code to mark the code as having a problem:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Property 'foo' does not exist on type 'TitleBar'.
Type 'true' is not assignable to type 'string'.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can even go further by doing the linting in the console and your continuous integration.&lt;/p&gt;

&lt;p&gt;All you need to do is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm i &lt;span class="nt"&gt;-D&lt;/span&gt; typescript


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And add this script to you package.json&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint:types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then we can execute it as:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm run lint:types


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will give you the same error as above but with a filepath and line number.&lt;/p&gt;

&lt;p&gt;So just by doing these few extra things your IDE can help you to stay type safe.&lt;/p&gt;

&lt;p&gt;Honestly, it will not be a gentle reminder - those red curly lines are hard to ignore and if you need some extra motivation you can hit F8 which will just throw the next error in your face :p.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffs3vsql3lt7e13vfkz1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffs3vsql3lt7e13vfkz1t.png" alt="showTypeErrors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;If you are like me you are probably wondering how does it know what properties are of which type? I certainly did not define any types yet!&lt;/p&gt;

&lt;p&gt;Typescript can make a lot of assumptions based on your ES6 code. The actual magic lays in the constructor:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are awesome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;darkMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I am dot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;title is obviously a string&lt;/li&gt;
&lt;li&gt;darkMode a boolean&lt;/li&gt;
&lt;li&gt;bar an object with x, y as number and title a string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So just by defining your initial values within the constructor most of your types should be good to go. 👍&lt;br&gt;
(Don't worry — I did not forget formatter, we'll get to it shortly)&lt;/p&gt;

&lt;p&gt;Types are already awesome but we can do even better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Look at the intellisense in VS Code.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rpwgd2c5qc1frw3rxf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rpwgd2c5qc1frw3rxf1.png" alt="intellisenseTitleTyped"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently it's really minimal... So let's add some JSDoc:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * The title to display inside the title bar
 * - should be less then 100 characters
 * - should not contain HTMl
 * - should be between 2-5 words
 *
 * @example
 * // DO:
 * el.title = 'Welcome to the jungle';
 *
 * // DON'T:
 * el.title = 'Info';
 * el.title = 'Welcome to &amp;lt;strong&amp;gt;the&amp;lt;/strong&amp;gt; jungle';
 * el.title = 'We like to talk about more then just what sees the eye';
 */&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are awesome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5rh8m15o2lj1y6dholy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5rh8m15o2lj1y6dholy.png" alt="intellisenseTitleTypedJsDoc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;much better 😊&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You do not need to add the &lt;code&gt;@type&lt;/code&gt; here as it's clear that it's a string and if you add it - it may get out of sync at some point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manually set types
&lt;/h3&gt;

&lt;p&gt;If we look at&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There is no way to see from this line alone what the property will hold.&lt;br&gt;
You could assign an empty/default function like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;but this does not make sense in all case.&lt;br&gt;
In our example, we would like to skip the formatting if there is no formatter function.&lt;br&gt;
Having a default function would defeat its purpose.&lt;br&gt;
In these cases, it's mandatory to provide a &lt;code&gt;@type&lt;/code&gt; and you can do so using JSDoc.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * You can provide a specific formatter that will change the way the title
 * gets displayed.
 *
 * *Note*: Changing the formatter does NOT trigger a rerender.
 *
 * @example
 * el.formatter = (value) =&amp;gt; `${value} for real!`;
 *
 * @type {Function}
 */&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That way if you provide a wrong type it will show an error.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Type 'false' is not assignable to type 'Function'.&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Also the immediately appearing &lt;code&gt;@example&lt;/code&gt; really makes it easy to create your own formatter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqr4irs2r3j7sf56pmtb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqr4irs2r3j7sf56pmtb.png" alt="intellisenseFormatterTypedJsDoc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup your own types and use them
&lt;/h3&gt;

&lt;p&gt;There is one more property that doesn't look too nice yet, and that is the &lt;code&gt;bar&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3aaut407ym65yg2ytbfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3aaut407ym65yg2ytbfw.png" alt="intellisenseBarTyped"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our type safety already works here, which is great, but we only know that x is a number; there is no additional info.&lt;br&gt;
We can improve this with JSDocs as well.&lt;/p&gt;

&lt;p&gt;So we define a special type called &lt;code&gt;Bar&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * This is a visible bar that gets displayed at the appropriate coordinates.
 * It has a height of 100%. An optional title can be provided.
 *
 * @typedef {Object} Bar
 * @property {number} x The distance from the left
 * @property {number} y The distance from the top
 * @property {string} [title] Optional title that will be set as an attribute (defaults to '')
 */&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Doing so we can also define certain properties as being optional.&lt;br&gt;
The only thing we need to do then is to assign it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * @type {Bar}
 */&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I am dot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe58jftvlf2pk67m0o2hj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe58jftvlf2pk67m0o2hj.png" alt="intellisenseBarTypedJsDoc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Add types to function parameters
&lt;/h3&gt;

&lt;p&gt;Let's create a simple format function which will allow for prefix/suffix by default and if you need more you can just override the &lt;code&gt;formatter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: this is not a super useful example but good enough for illustration purposes&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;formattedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;formattedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;formattedValue&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Again just by using default options it already knows all the types.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffg9tk94inmk0a01vcm3u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffg9tk94inmk0a01vcm3u.png" alt="intellisenseFormatTyped"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So just adding a little documentation is probably all you need.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * This function can prefix/suffix your string.
 *
 * @example
 * el.format('foo', { prefix: '...' });
 */&lt;/span&gt;
&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgjg8fn3c8rws9o6m0zh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgjg8fn3c8rws9o6m0zh.png" alt="intellisenseFormatTypedJsDocsOnlyDescription"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or if you want to have a union type (e.g. allow strings AND numbers).&lt;br&gt;
Be sure to only document what you actually need as with this method you override the default types and that means things could get out of sync.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * This function can prefix/suffix your string.
 *
 * @example
 * el.format('foo', { prefix: '...' });
 *
 * @param {string|number} value String to format
 */&lt;/span&gt;
&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F203m0frww98fz8azh9zm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F203m0frww98fz8azh9zm.png" alt="intellisenseFormatTypedJsDoc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you really need to add very specific descriptions to every object options then you need to duplicate the typings.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * This function can prefix/suffix your string.
 *
 * @example
 * el.format('foo', { prefix: '...' });
 *
 * @param {string} value String to format
 * @param {Object} opts Options
 * @param {string} opts.prefix Mandatory and will be added before the string
 * @param {string} [opts.suffix] Optional and will be added after the string
 */&lt;/span&gt;
&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtd74yriipk05v0l3rak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtd74yriipk05v0l3rak.png" alt="intellisenseFormatTypedJsDocExtraAllOptions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing Types across files
&lt;/h3&gt;

&lt;p&gt;Files never live in isolation so there might come a point where you want to use a type within another location.&lt;br&gt;
Let's take our good old friend the ToDo List as an example.&lt;br&gt;
You will have &lt;code&gt;todo-item.js&lt;/code&gt; &amp;amp; &lt;code&gt;todo-list.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The item will have a constructor like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * What you need to do
   */&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * How important is it? 1-10
   *
   * 1 = less important; 10 = very important
   */&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Is this task done already?
   */&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So how can I reuse those type in &lt;code&gt;todo-list.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's assume the following structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;todo-list&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;todo-item&lt;/span&gt; &lt;span class="na"&gt;.label=&lt;/span&gt;&lt;span class="s"&gt;${One}&lt;/span&gt; &lt;span class="na"&gt;.priority=&lt;/span&gt;&lt;span class="s"&gt;${5}&lt;/span&gt; &lt;span class="na"&gt;.done=&lt;/span&gt;&lt;span class="s"&gt;${true}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/todo-item&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;todo-item&lt;/span&gt; &lt;span class="na"&gt;.label=&lt;/span&gt;&lt;span class="s"&gt;${Two}&lt;/span&gt; &lt;span class="na"&gt;.priority=&lt;/span&gt;&lt;span class="s"&gt;${8}&lt;/span&gt; &lt;span class="na"&gt;.done=&lt;/span&gt;&lt;span class="s"&gt;${false}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/todo-item&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/todo-list&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and we would like to calculate some statistics.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;calculateStats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;doneCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;prioritySum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;doneCounter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;prioritySum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Done tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doneCounter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Average priority&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prioritySum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above code actually has an error in it 😱&lt;br&gt;
&lt;code&gt;item.prio&lt;/code&gt; does not exists. Types could have saved us here, but how?&lt;/p&gt;

&lt;p&gt;First let's import the type&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * @typedef {import('./todo-item.js').ToDoItem} ToDoItem
 */&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and then we type cast it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** @type {ToDoItem[]} */&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And there we already see the type error 💪&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg3gm4q8npydqxprenz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg3gm4q8npydqxprenz6.png" alt="importCast"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Data Objects to create Custom Elements
&lt;/h3&gt;

&lt;p&gt;In most cases, we do not only want to access an existing DOM and type cast the result but we would like to actually render those elements from a data array.&lt;/p&gt;

&lt;p&gt;Here is the example array&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and then we render it&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;todo-item .label=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; .priority=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; .done=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/todo-item&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;How can we make this type safe?&lt;/p&gt;

&lt;p&gt;Unfortunately, simply casting it via &lt;code&gt;@type {ToDoItem[]}&lt;/code&gt; does not really work out 😭&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhse3bllxf90tbuoltzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhse3bllxf90tbuoltzh.png" alt="ElementAsObjectFail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It expects the object to be a full representation of an HTMLElement and of course our little 3 property object does miss quite some properties there.&lt;/p&gt;

&lt;p&gt;What we can do is to have a &lt;code&gt;Data Representation&lt;/code&gt; of our web component. e.g. define what is needed to create such an element in the dom.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * Object Data representation of ToDoItem
 *
 * @typedef {Object} ToDoItemData
 * @property {string} label
 * @property {number} priority
 * @property {Boolean} done
 */&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can then import and type cast it&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * @typedef {import('./todo-item.js').ToDoItemData} ToDoItemData
 * @typedef {import('./todo-item.js').ToDoItem} ToDoItem
 */&lt;/span&gt;

&lt;span class="c1"&gt;// [...]&lt;/span&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * @type {ToDoItemData[]}
   */&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And 🎉 type safety for web component AND its data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bhl8lxdfo93kjxj84gs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bhl8lxdfo93kjxj84gs.png" alt="ItemDataTypeErrors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let your users consume your types
&lt;/h2&gt;

&lt;p&gt;One thing that is a little tougher if you have types not as definition files is how you can make them available.&lt;/p&gt;

&lt;p&gt;Generally speaking, you will need to ask your users to add a &lt;code&gt;tsconfig.json&lt;/code&gt; like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"es2017"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checkJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitThis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alwaysStrict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"**/*.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/&amp;lt;your-package-name&amp;gt;/**/*.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/!(&amp;lt;your-package-name&amp;gt;)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The important part is the &lt;code&gt;include&lt;/code&gt; and not &lt;code&gt;exclude&lt;/code&gt; of your package name.&lt;/p&gt;

&lt;p&gt;If you think that is a little complicated you are right. There &lt;a href="https://github.com/Microsoft/TypeScript/issues/29824" rel="noopener noreferrer"&gt;are ideas&lt;/a&gt; to improve this flow however it seemed to not have gotten much attention lately - Give it your thumbs up and join the conversation.&lt;/p&gt;

&lt;p&gt;For full TypeScript project you might want to do a little more like have 2 &lt;code&gt;tsconfigs.json&lt;/code&gt; one for linting and one for buildling (as allowJs prevent automatic creation of definition files).&lt;/p&gt;

&lt;p&gt;You can find more details about such an approach at &lt;a href="https://open-wc.org/developing/types.html#setup-for-typescript" rel="noopener noreferrer"&gt;Setup For Typescript on Open Web Components&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick recap:
&lt;/h2&gt;

&lt;p&gt;Equipped with these options for properties/functions you should be fine for most web components.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set defaults for properties in constructor and the type will be there automatically&lt;/li&gt;
&lt;li&gt;If you do not have a default make sure to add &lt;code&gt;@types&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add additional information/docs/examples as JSDoc for a nicer developer experience&lt;/li&gt;
&lt;li&gt;Make sure to type cast your dom results&lt;/li&gt;
&lt;li&gt;Add type linting via console/continuous integration to make sure they are correct&lt;/li&gt;
&lt;li&gt;Inform your users how they can consume your types&lt;/li&gt;
&lt;li&gt;Bookmark the &lt;a href="https://github.com/Microsoft/TypeScript/wiki/JSDoc-support-in-JavaScript" rel="noopener noreferrer"&gt;Typescript JSDoc Reference&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need more information on additional JSDoc features for types take a look at &lt;a href="https://medium.com/@trukrs/type-safe-javascript-with-jsdoc-7a2a63209b76" rel="noopener noreferrer"&gt;Type Safe JavaScript with JSDoc&lt;/a&gt;. I highly recommend reading it!&lt;/p&gt;

&lt;p&gt;The full code can be found on &lt;a href="https://github.com/daKmoR/type-safe-webcomponents-with-jsdoc" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;br&gt;
To see how your users will get it look at the &lt;a href="https://github.com/daKmoR/type-safe-webcomponents-with-jsdoc/blob/master/test/title-bar.test.js" rel="noopener noreferrer"&gt;tests&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;These are steps that can help make web components simpler and saver to use.&lt;/li&gt;
&lt;li&gt;Not everything here is useful for every situation and there will be definitely situations where we don't have a recipe yet.&lt;/li&gt;
&lt;li&gt;If you encounter any issues (hopefully + solution) please let us know and we will add it to this "Cookbook for types with web components".&lt;/li&gt;
&lt;li&gt;VS Code is working on making a way to bring autocomplete to declarative html by having a definition for web components attribute - See the &lt;a href="https://github.com/w3c/webcomponents/issues/776" rel="noopener noreferrer"&gt;proposal&lt;/a&gt; to allow for getting errors if undefined attributes are used:
```
&lt;/li&gt;
&lt;/ul&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Follow me on [Twitter](https://twitter.com/daKmoR).
If you have any interest in web component make sure to check out [open-wc.org](https://open-wc.org).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webcomponents</category>
      <category>javascript</category>
      <category>type</category>
      <category>litelement</category>
    </item>
  </channel>
</rss>
