<?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: Lars den Bakker</title>
    <description>The latest articles on DEV Community by Lars den Bakker (@larsdenbakker).</description>
    <link>https://dev.to/larsdenbakker</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%2F202298%2Fc7b0f6d7-1bb9-4bad-85a6-860c2c5040ab.jpg</url>
      <title>DEV Community: Lars den Bakker</title>
      <link>https://dev.to/larsdenbakker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/larsdenbakker"/>
    <language>en</language>
    <item>
      <title>Developing Without a Build (2): es-dev-server</title>
      <dc:creator>Lars den Bakker</dc:creator>
      <pubDate>Sun, 04 Aug 2019 19:28:59 +0000</pubDate>
      <link>https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5</link>
      <guid>https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5</guid>
      <description>&lt;h1&gt;
  
  
  Developing Without a Build: es-dev-server
&lt;/h1&gt;

&lt;p&gt;This article is part of a series on developing without a build:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5"&gt;es-dev-server (this article)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Testing (coming soon!)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao"&gt;introduction article&lt;/a&gt;, we looked into different workflows and browser APIs. In this article, we will look into how we can set up &lt;code&gt;es-dev-server&lt;/code&gt;, and how it can help us developing without a build.&lt;/p&gt;

&lt;h2&gt;
  
  
  es-dev-server
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/open-wc/open-wc/tree/master/packages/es-dev-server"&gt;es-dev-server&lt;/a&gt; is a composable web server that focuses on developer productivity when developing without a build step. Through options, you can opt into extra features such as caching, reloading on file changes, SPA routing, resolving bare module imports and compatibility modes to support older browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;To start off let's create an empty npm project and install &lt;code&gt;es-dev-server&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; es-dev-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create an &lt;code&gt;index.html&lt;/code&gt; in the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;&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;"app"&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 &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;"./src/app.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 create a dummy &lt;code&gt;src/app.js&lt;/code&gt; file:&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;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;hello world&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;We can now run our application with &lt;code&gt;es-dev-server&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Without any extra flags, &lt;code&gt;es-dev-server&lt;/code&gt; acts like a regular static file server. Any extra functionality needs to be enabled explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bare imports
&lt;/h2&gt;

&lt;p&gt;One of the first things you will run into when developing without any build tools is how to deal with bare module imports 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="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&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;Out of the box, the browser will throw an error, as it doesn't know how to handle these kinds of imports. In our &lt;a href="https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao"&gt;previous article&lt;/a&gt; we explored how to use these imports by using import maps, an upcoming browser API.&lt;/p&gt;

&lt;p&gt;Until import maps are properly supported in browsers, we can use the &lt;code&gt;--node-resolve&lt;/code&gt; flag of the dev server. This will rewrite imports in your modules using NodeJS module resolution before serving them to the browser.&lt;/p&gt;

&lt;p&gt;To see how this works let's add &lt;code&gt;lit-html&lt;/code&gt;, a HTML templating library, to our project:&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; lit-html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Change &lt;code&gt;src/app.js&lt;/code&gt; to import it:&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;render&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Hello world!&amp;lt;/p&amp;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="nx"&gt;template&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&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 let's restart our server, adding the node resolve flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--node-resolve&lt;/span&gt; &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you inspect the network tab, you will see the modules are served correctly as expected. &lt;code&gt;src/app.js&lt;/code&gt; is rewritten to:&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;render&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;./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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Hello world!&amp;lt;/p&amp;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="nx"&gt;template&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&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;h2&gt;
  
  
  Watch mode
&lt;/h2&gt;

&lt;p&gt;A great productivity booster is reloading the browser while you are editing files. &lt;/p&gt;

&lt;p&gt;To enable this option, restart the server with the &lt;code&gt;watch&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--watch&lt;/span&gt; &lt;span class="nt"&gt;--node-resolve&lt;/span&gt; &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now change anything inside one of the served files, for example the rendered html in &lt;code&gt;app.js&lt;/code&gt;. You will see the browser reload automatically with the updated code.&lt;/p&gt;

&lt;p&gt;Reloads are done using the &lt;code&gt;EventSource&lt;/code&gt; API, which is not supported on Edge and IE11. The dev server injects a small script, which connects to a message channel endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--imLkAHHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pqtk7vbxxkm3d88lfgxs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--imLkAHHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pqtk7vbxxkm3d88lfgxs.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;es-dev-server&lt;/code&gt; uses the file system's last modified timestamp to return a 304 if the file hasn't changed. This significantly speeds up reloads. You can test this in your browser by turning off &lt;code&gt;Disable cache&lt;/code&gt; and refreshing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---9FdBitG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qszke0x58csc9eu9337g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---9FdBitG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qszke0x58csc9eu9337g.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Folder structure
&lt;/h2&gt;

&lt;p&gt;Not every project has a single &lt;code&gt;index.html&lt;/code&gt; in the root of the project. Because &lt;code&gt;es-dev-server&lt;/code&gt; works just like a regular web server, it can serve files from any folder.&lt;/p&gt;

&lt;p&gt;For example, let's create a new folder called &lt;code&gt;demo&lt;/code&gt;, and move our &lt;code&gt;index.html&lt;/code&gt; inside it.&lt;/p&gt;

&lt;p&gt;We will need to adjust the script src path to reflect this change:&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;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;"../src/app.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;And we need to tell the server to open inside the demo folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--node-resolve&lt;/span&gt; &lt;span class="nt"&gt;--open&lt;/span&gt; /demo/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The application should be displayed without any changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing the root dir
&lt;/h3&gt;

&lt;p&gt;We might be tempted to change the root directory of the web server in order to get rid of the &lt;code&gt;/demo/&lt;/code&gt; part in the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--root-dir&lt;/span&gt; /demo/ &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;However, this won't work because the web server can only serve files that are within its root directory. By default, this is the current working directory. In our case, the web server needs to be able to serve the contents of the &lt;code&gt;src&lt;/code&gt; folder, as well as the &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;This is a common problem when working in a monorepo when you want to serve files from a package subdirectory. Many of the modules you need to serve are in the root of the project, so you need to move the root directory up two levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--root-dir&lt;/span&gt; ../../ &lt;span class="nt"&gt;--open&lt;/span&gt; packages/my-package/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  SPA Routing
&lt;/h2&gt;

&lt;p&gt;If you are building a Single Page Application you are likely doing some form of front-end routing. In order to enable deeplinking or refreshing, the web server should return your &lt;code&gt;index.html&lt;/code&gt; on deeper paths. This is sometimes called history API fallback.&lt;/p&gt;

&lt;p&gt;Setting up a router is beyond the scope of this article, but the option is easy to enable using the &lt;code&gt;--app-index&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--node-resolve&lt;/span&gt; &lt;span class="nt"&gt;--app-index&lt;/span&gt; index.html &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When using the &lt;code&gt;--app-index&lt;/code&gt; flag, the server will automatically open the server on your app's index if you don't pass an explicit path to &lt;code&gt;--open&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compatibility with older browsers
&lt;/h2&gt;

&lt;p&gt;Although we can use the latest versions of the major browsers for development, we still need to support older browsers in production. We also might be using some new browsers features that are not yet supported in the latest version of all the major browsers.&lt;/p&gt;

&lt;p&gt;It would be a shame if we have to run a production build every time we want to run our app on one of these browsers. &lt;code&gt;es-dev-server&lt;/code&gt; supports multiple compatibility modes that help with this.&lt;/p&gt;

&lt;p&gt;When compatibility mode is enabled, the server handles the necessary polyfills and code transformations for older browsers. This takes us into build tooling territory, so we're no longer purely "developing without build tools". I think that's fine, as we're using it only for browser compatibility. You have to opt-in to this behavior explicitly.&lt;/p&gt;

&lt;p&gt;Let's see how it works in action. Add a dynamic import to &lt;code&gt;app.js&lt;/code&gt; to lazy load a module when a button is clicked:&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;render&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;lazyLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&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;lit-html/directives/until.js&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;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;button @click=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lazyLoad&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;Click me!&amp;lt;/button&amp;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="nx"&gt;template&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&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;The dynamic import does not really do anything functional. If we run this on Chrome, Safari, and Firefox it works just fine. Because Edge does not yet support dynamic imports we can't run this code there.&lt;/p&gt;

&lt;p&gt;We can turn on the lightest compatibility mode, &lt;code&gt;esm&lt;/code&gt;, to handle this case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx es-dev-server &lt;span class="nt"&gt;--node-resolve&lt;/span&gt; &lt;span class="nt"&gt;--compatibility&lt;/span&gt; esm &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;esm&lt;/code&gt; enabled, &lt;code&gt;es-dev-server&lt;/code&gt; injects &lt;a href="https://www.npmjs.com/package/es-module-shims"&gt;es-module-shims&lt;/a&gt; and adds a loader script to your index. You don't need to change any of your code for this. You can view the injected script in the index file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4iZ1QNUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/l8kome6iuhstjb8p4ycr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4iZ1QNUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/l8kome6iuhstjb8p4ycr.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is some extra boilerplate, as the other compatibility modes might add more polyfills. The polyfills are hashed so that they can be cached aggressively in the browser.&lt;/p&gt;

&lt;p&gt;Besides &lt;code&gt;esm&lt;/code&gt; there are the &lt;code&gt;modern&lt;/code&gt; and &lt;code&gt;all&lt;/code&gt; compatibility modes. These modes inject polyfills for common browser APIs and use &lt;code&gt;@babel/preset-env&lt;/code&gt; for transforming the latest javascript syntax to a compatible format.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;modern&lt;/code&gt; compatibility mode your code is made compatible with the latest two versions of Chrome, Safari, Firefox, and Edge.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;all&lt;/code&gt; compatibility mode support is extended to older browsers, all the way to IE11. On browsers which don't support es modules, they are transformed to &lt;a href="https://www.npmjs.com/package/systemjs"&gt;systemjs modules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The transformations slow down the server a bit, so I don't recommend using &lt;code&gt;modern&lt;/code&gt; or &lt;code&gt;all&lt;/code&gt; mode during regular development. You can create separate scripts in your &lt;code&gt;package.json&lt;/code&gt;, and run in compatibility mode only when you view your app on older browsers. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;esm&lt;/code&gt; mode has a negligible effect on performance, so that should be fine to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import maps
&lt;/h2&gt;

&lt;p&gt;In the previous article, we briefly discussed import maps as an upcoming browser feature which handles bare module imports. &lt;code&gt;es-module-shims&lt;/code&gt; and &lt;code&gt;systemjs&lt;/code&gt; both support import maps. If compatibility mode is enabled, the dev server takes care of adapting your import maps to work with these libraries, making this a viable workflow during development.&lt;/p&gt;

&lt;p&gt;The import maps API is not fully stabilized yet, so if you're going down this path it's good to keep an eye how this standard evolves. &lt;a href="https://dev.to/open-wc/on-the-bleeding-edge-3cb8"&gt;Check out this article&lt;/a&gt; for a workflow using import maps.&lt;/p&gt;

&lt;h2&gt;
  
  
  More options
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/open-wc/open-wc/tree/master/packages/es-dev-server"&gt;Check out the official documentation&lt;/a&gt; for more options, such as integrating the dev server as a library with other tools and installing custom middlewares.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;To get started with a project that sets up the dev server for you, use the &lt;code&gt;open-wc&lt;/code&gt; project scaffolding:&lt;br&gt;
&lt;/p&gt;

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



&lt;p&gt;It sets up the project with &lt;code&gt;lit-element&lt;/code&gt;, a web component library. You can swap this for any library of your choosing, the setup is not specific to web components.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>buildless</category>
      <category>webcomponents</category>
      <category>lithtml</category>
    </item>
    <item>
      <title>Developing Without a Build (1): Introduction</title>
      <dc:creator>Lars den Bakker</dc:creator>
      <pubDate>Sun, 04 Aug 2019 19:16:12 +0000</pubDate>
      <link>https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao</link>
      <guid>https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao</guid>
      <description>&lt;h1&gt;
  
  
  Developing Without a Build: Introduction
&lt;/h1&gt;

&lt;p&gt;This article is part of a series on developing without a build:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao"&gt;Introduction (this article)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5"&gt;es-dev-server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Testing (coming soon!)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this article, we explore why and if we should do development without a build step, and we give an overview of the current and future browser APIs that make this possible. In the followup articles, we look into how &lt;code&gt;es-dev-server&lt;/code&gt; can help us with that and how to handle testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern web development
&lt;/h2&gt;

&lt;p&gt;In the early days of web development, all we needed was a simple file editor and a web server. It was easy for newcomers to understand the process and get started making their own web pages. Web development has changed a lot since then: the complexity of the tools we use for development has grown just as much as the complexity of the things that we're building on the web.&lt;/p&gt;

&lt;p&gt;Imagine what it's like if you're coming in completely new to web development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You first need to learn a lot of different tools and understand how each of them is changing your code before it can actually run in the browser.&lt;/li&gt;
&lt;li&gt;Your IDE and linter likely do not understand the syntax of this framework that was recommended to you by a friend, so you need to find the right mix of plugins that makes it work.&lt;/li&gt;
&lt;li&gt;Source maps need to be configured properly for all of the tools in the chain if you want to have any chance of debugging your code in the browser. Getting them to work with your tests is a whole other story.&lt;/li&gt;
&lt;li&gt;You decided to keep things simple and not use typescript. You're following the tutorials and but can't get this decorators thing to work and the error messages are not helping. Turns out you didn't configure your babel plugins in the correct order...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It may sound exaggerated, and I know that there are very good starter projects and tutorials out there, but this experience is common to a lot of developers. You may have jumped through similar hoops yourself.&lt;/p&gt;

&lt;p&gt;I think that's really a shame. One of the key selling points of the web is that it's an easy and open format. It should be easy to just get started right away without a lot of configuration and ceremony.&lt;/p&gt;

&lt;p&gt;I'm not criticizing the build tools themselves, they all have a role and a purpose. And for a long time, using a build was the only real way to actually create complex applications on the web. Web standards and browser implementations were just not there to support modern web development. Build tools have really helped push web development forward.&lt;/p&gt;

&lt;p&gt;But browsers have improved a lot in the past years, and there are many exciting things to come in the near future. I think that now is a good time to consider if we can do away with a big part of the tooling complexity, at least during development. Maybe not yet for all types of projects, but let's see how far we can go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading modules in the browser
&lt;/h2&gt;

&lt;p&gt;This is not a step by step tutorial, but you can follow along with any of the examples by using any web server. For example &lt;code&gt;http-server&lt;/code&gt; from npm. Run it with &lt;code&gt;-c-1&lt;/code&gt; to disable time-based caching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx http-server &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-c-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Loading modules
&lt;/h3&gt;

&lt;p&gt;Modules can be loaded in the browser using regular script tags with a &lt;code&gt;type="module"&lt;/code&gt; attribute. We can just write our module code directly inline:&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;&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;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="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;hello world!&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;/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;From here we can use static imports to load other modules:&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;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;./app.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;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;hello world!&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we need to use an explicit file extension, as the browser doesn't know which file to request otherwise.&lt;/p&gt;

&lt;p&gt;The same thing works if we use the &lt;code&gt;src&lt;/code&gt; attribute:&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;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;"./app.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;h3&gt;
  
  
  Loading dependencies
&lt;/h3&gt;

&lt;p&gt;We don't write our code in just one file. After importing the initial module, we can import other modules. For example, let's create two new files:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&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;./message.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;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="s2"&gt;`The message is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&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;&lt;code&gt;src/message.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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello world&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;Place both files in a &lt;code&gt;src&lt;/code&gt; directory and import &lt;code&gt;app.js&lt;/code&gt; from your index.html:&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;&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;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;"./src/app.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;If you run this and check the network panel, you will see both modules being loaded. Because imports are resolved relatively, &lt;code&gt;app.js&lt;/code&gt; can refer to &lt;code&gt;message.js&lt;/code&gt; using a relative path:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fyp0n86hkkxilbp29k974.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fyp0n86hkkxilbp29k974.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This seems trivial, but it is extremely useful and something we did not have before with classic scripts. We no longer need to coordinate dependencies somewhere central or maintain a base URL. Modules can declare their own dependencies, and we can import any module without knowing what their dependencies are. The browser takes care of requesting the correct files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic imports
&lt;/h3&gt;

&lt;p&gt;When building any serious web application we are usually going to need to do some form of lazy loading for best performance. Static imports like we saw before cannot be used conditionally, they always need to exist at the top level.&lt;/p&gt;

&lt;p&gt;For example, we cannot write:&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="nx"&gt;someCondition&lt;/span&gt;&lt;span class="p"&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;./bar.js&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;This is what dynamic imports are for. Dynamic imports can import a module at any time. It returns a Promise that resolves with the imported module.&lt;/p&gt;

&lt;p&gt;For example let's update the &lt;code&gt;app.js&lt;/code&gt; example we created above:&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="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;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;./message.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;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="s2"&gt;`The message is: &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;message&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;Now we are not importing the message module right away, but delaying it until the user has clicked anywhere on the page. We can await the promise returned from the import and interact with the module that was returned. Any exported members are available on the module object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy evaluation
&lt;/h3&gt;

&lt;p&gt;This is where developing without a bundler has a significant benefit. If you bundle your application before serving it to the browser, the bundler needs to evaluate all your dynamic imports to do code splitting and output separate chunks. For large applications with a lot of dynamic imports, this can add significant overhead as the entire application is built and bundled before you can see anything in the browser.&lt;/p&gt;

&lt;p&gt;When serving unbundled modules, the entire process is lazy. The browser only does the necessary work to load the modules that were actually requested.&lt;/p&gt;

&lt;p&gt;Dynamic imports are supported by the latest versions of Chrome, Safari, and Firefox. It's not supported in the current version of Edge but will be supported by the new Chromium-based Edge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Import" rel="noopener noreferrer"&gt;Read more about dynamic imports at MDN&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-relative requests
&lt;/h3&gt;

&lt;p&gt;Not all browser APIs resolve requests relative to the module's location. For example when using fetch or when rendering images on the page.&lt;/p&gt;

&lt;p&gt;To handle these cases we can use &lt;code&gt;import.meta.url&lt;/code&gt; to get information about the current module's location.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import.meta&lt;/code&gt; is a special object which contains metadata about the currently executing module. &lt;code&gt;url&lt;/code&gt; is the first property that's exposed here, and works a lot like &lt;code&gt;__dirname&lt;/code&gt; in NodeJS.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import.meta.url&lt;/code&gt; points to the url the module was imported with:&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;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="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// logs http://localhost:8080/path/to/my/file.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the &lt;code&gt;URL&lt;/code&gt; API for easy URL building. For example to request a JSON file:&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;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// becomes http://localhost:8080/path/to/my/translations/en-US.json&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;translationsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./translations/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;translationsPath&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://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta" rel="noopener noreferrer"&gt;Read more about import.meta at MDN&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading other packages
&lt;/h3&gt;

&lt;p&gt;When building an application you will quickly run into having to include other packages from npm. This also works just fine in the browser. For example, let's install and use lodash:&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;-P&lt;/span&gt; lodash-es
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;kebabCase&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/lodash-es/kebabCase.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;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="nf"&gt;kebabCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;camelCase&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;Lodash is a very modular library and the &lt;code&gt;kebabCase&lt;/code&gt; function depends on a lot of other modules. These dependencies are taken care of automatically, the browser resolves and imports them for you:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw6mk8ox4pnotaa1gxzu7.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw6mk8ox4pnotaa1gxzu7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Writing explicit paths to your node modules folder is a bit unusual. While it is valid and it can work, most people are used to writing what's called a bare import specifier:&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;kebabCase&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;lodash-es&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="nx"&gt;kebabCase&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;lodash-es/kebabCase.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;This way you don't specifically say where a package is located, only what it's called. This is used a lot by NodeJS, whose resolver will walk the file system looking for &lt;code&gt;node_modules&lt;/code&gt; folders and packages by that name. It reads the &lt;code&gt;package.json&lt;/code&gt; to know which file to use. &lt;/p&gt;

&lt;p&gt;The browser cannot afford to send a bunch of requests until it stops getting 404s, that would be way too expensive. Out of the box, the browser will just throw an error when it sees a bare import. There is a &lt;a href="https://github.com/WICG/import-maps" rel="noopener noreferrer"&gt;new browser API called import maps&lt;/a&gt; which lets you instruct the browser how to resolve these imports:&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;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;lodash-es&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/lodash-es/lodash.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;lodash-es/&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/lodash-es/&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;p&gt;It's currently &lt;a href="https://developers.google.com/web/updates/2019/03/kv-storage#import_maps" rel="noopener noreferrer"&gt;implemented in chrome behind a flag&lt;/a&gt;, and it's easy to shim on other browsers &lt;a href="https://www.npmjs.com/package/es-module-shims" rel="noopener noreferrer"&gt;with es-module-shims&lt;/a&gt;. Until we get broad browser support, that can be an interesting option during development.&lt;/p&gt;

&lt;p&gt;It's still quite early for import maps, and for most people, they may still be a bit too bleeding edge. If you are interested in this workflow &lt;a href="https://dev.to/open-wc/on-the-bleeding-edge-3cb8"&gt;I recommend reading this article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Until import maps are properly supported, the recommended approach is to use a web server which rewrites the bare imports into explicit paths on the fly before serving modules to the browser. There are some servers available that do this. I recommend &lt;a href="https://www.npmjs.com/package/es-dev-server" rel="noopener noreferrer"&gt;es-dev-server&lt;/a&gt; which we will explore in &lt;a href="https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5"&gt;the next article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;Because we aren't bundling all of our code into just a few files, we don't have to set up any elaborate caching strategies. Your web server can use the file system's last modified timestamp to return a 304 if the file hasn't changed.&lt;/p&gt;

&lt;p&gt;You can test this in your browser by turning off &lt;code&gt;Disable cache&lt;/code&gt; and refreshing:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fikcy8cjivd1c5zr4j2nd.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fikcy8cjivd1c5zr4j2nd.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Non-js modules
&lt;/h2&gt;

&lt;p&gt;So far we've only looked into javascript modules, and the story is looking pretty complete. It looks like we have most of the things we need to write javascript at scale. But on the web we're not just writing javascript, we need to deal with other languages as well.&lt;/p&gt;

&lt;p&gt;The good news is that there are concrete proposals for HTML, CSS and JSON modules, and all major browser vendors seem to be supportive of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/whatwg/html/issues/4315" rel="noopener noreferrer"&gt;json modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/w3c/webcomponents/issues/645" rel="noopener noreferrer"&gt;html modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/w3c/webcomponents/issues/759" rel="noopener noreferrer"&gt;css modules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bad news is that they're not available just yet, and it's not clear when they will be. We have to look for some solutions in the meantime.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON
&lt;/h3&gt;

&lt;p&gt;In Node JS it's possible to import JSON files from javascript. These become available as javascript objects. In web projects, this is used frequently as well. There are many build tool plugins to make this possible.&lt;/p&gt;

&lt;p&gt;Until browsers support JSON modules, we can either just use a javascript module which exports an object or we can use fetch to retrieve the JSON files. See the &lt;code&gt;import.meta.url&lt;/code&gt; section for an example which uses fetch.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;

&lt;p&gt;Over time web frameworks have solved HTML templating in different ways, for example by placing HTML inside javascript strings. JSX is a very popular format for embedding dynamic HTML inside javascript, but it is not going to run natively in the browser without some kind of transformation.&lt;/p&gt;

&lt;p&gt;If you really want to author HTML in HTML files, until we get HTML modules, you could use &lt;code&gt;fetch&lt;/code&gt; to download your HTML templates before using it with whatever rendering system you are using. I don't recommend this because it's hard to optimize for production. You want something that can be statically analyzed and optimized by a bundler so that you don't spawn a lot of requests in production.&lt;/p&gt;

&lt;p&gt;Luckily there is a great option available. With es2015/es6 we can use tagged template string literals to embed HTML inside JS, and use it for doing efficient DOM updates. Because HTML templating often comes with a lot of dynamism, it is actually a great benefit that we can use javascript to express this instead of learning a whole new meta syntax. It runs natively in the browser, has a great developer experience and integrates with your module graph so it can be optimized for production.&lt;/p&gt;

&lt;p&gt;There are some really good production ready and feature complete libraries that can be used for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/htm" rel="noopener noreferrer"&gt;htm, JSX using template literals. Works with libraries that use JSX, such as react&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/lit-html" rel="noopener noreferrer"&gt;lit-html, a HTML templating library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/lit-element" rel="noopener noreferrer"&gt;lit-element, integrates lit-html with web components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/haunted" rel="noopener noreferrer"&gt;haunted, a functional web components library with react-like hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/hybrids" rel="noopener noreferrer"&gt;hybrids, another functional web component library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/hyperhtml" rel="noopener noreferrer"&gt;hyperHTML, a HTML templating library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For syntax highlighting you might need to configure your IDE or install a plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS
&lt;/h3&gt;

&lt;p&gt;For HTML and JSON there are sufficient alternatives. Unfortunately, with CSS it is more complicated. By itself, CSS is not modular as it affects the entire page. A common complaint is that this is what makes CSS so difficult to scale.&lt;/p&gt;

&lt;p&gt;There a lot of different ways to write CSS, it's beyond the scope of this article to look into all of them. Regular stylesheets will work just fine if you load them in your index.html. If you are using some kind of CSS preprocessor you can run it before running your web server and just load the CSS output. &lt;/p&gt;

&lt;p&gt;Many CSS in JS solutions should also work if the library publishes an es module format that you can import.&lt;/p&gt;

&lt;h4&gt;
  
  
  Shadow dom
&lt;/h4&gt;

&lt;p&gt;For truly modular CSS I recommend looking into &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" rel="noopener noreferrer"&gt;Shadow dom&lt;/a&gt;, it fixes many of the scoping and encapsulation problems of CSS. I've used it with success in many different types of projects but it's good to mention that it's not yet a complete story. There are still missing features that are being worked out in the standard so it may not yet be the &lt;a href="https://dev.to/webpadawan/beyond-the-polyfills-how-web-components-affect-us-today-3j0a"&gt;right solution in all scenarios&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Good to mention here is &lt;a href="https://www.npmjs.com/package/lit-element" rel="noopener noreferrer"&gt;the lit-element library&lt;/a&gt;, which offers a great developer experience when authoring modular CSS without a build step. &lt;code&gt;lit-element&lt;/code&gt; does most of the heavy lifting for you. You author CSS using tagged template literals, which is just syntax sugar for creating a &lt;a href="https://developers.google.com/web/updates/2019/02/constructable-stylesheets" rel="noopener noreferrer"&gt;Constructable Stylesheet&lt;/a&gt;. This way you can write and share CSS between your components.&lt;/p&gt;

&lt;p&gt;This system will also integrate well with CSS modules when they are shipped. We could emulate CSS modules by using fetch, but as we saw with HTML it's hard to optimize this for production use. I'm not a fan of CSS in JS, but lit-element's solution is different and very elegant. You're writing CSS in a JS file, but it's still valid CSS syntax. If you like to keep things separated, you can just create a my-styles.css.js file and use a default export of just a stylesheet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Library support
&lt;/h3&gt;

&lt;p&gt;Luckily the amount of libraries shipping es module format is growing steadily. But there are still popular libraries which only ship UMD or CommonJS. These don't work without some kind of code transformation. The best thing we can do is open issues on these projects to give them an indication of how many people are interested in supporting the native module syntax.&lt;/p&gt;

&lt;p&gt;I think this is a problem that will disappear relatively quickly, especially after Node JS finishes their es modules implementation. Many projects already use es modules as their authoring format, and I don't think anyone really likes having to ship multiple imperfect module formats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;The goal of this article is to explore workflows where we don't need to do any building for development, and I think we've shown that there are real possibilities. For a lot of use cases, I think we can do away with most of the tooling for development. In other cases, I think they can still be useful. But I think our starting point should be reversed. Instead of trying to make our production builds work during development, we should be writing standard code that runs in the browser as is and only perform light transformations if we think it's necessary.&lt;/p&gt;

&lt;p&gt;It's important to reiterate that I don't think that build tools are evil, and I am not saying that this is the right approach for every project. That is a choice that each team should make for themselves based on their own requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  es-dev-server
&lt;/h2&gt;

&lt;p&gt;You can do almost everything described in this article with any regular web server. That being said, there are still web server features which can really help with the development experience. Especially if we want to run our applications on older browsers, we might need some help.&lt;/p&gt;

&lt;p&gt;At &lt;code&gt;open-wc&lt;/code&gt; we created &lt;a href="https://www.npmjs.com/package/es-dev-server" rel="noopener noreferrer"&gt;es-dev-server&lt;/a&gt;, a composable web server that focuses on developer productivity when developing without a build step. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5"&gt;Check out our next article &lt;/a&gt; to see how we can set it up!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;To get started with developing without any build tools, you can use the &lt;code&gt;open-wc&lt;/code&gt; project scaffolding to set up the basics:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It sets up the project with &lt;code&gt;lit-element&lt;/code&gt;, a web component library. You can swap this for any library of your choosing, the setup is not specific to web components.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>buildless</category>
      <category>webcomponents</category>
      <category>importmaps</category>
    </item>
  </channel>
</rss>
