 All right. Hi, everyone. Thanks for coming. I hope everyone's having a really great Google I.O. My name is Justin Finiani, and I'm the tech lead for the Polymer Tools team, which means that right now we are going to talk all about developer tooling for web components. So the Polymer Tools team maintains a whole suite of tools for web components, developers, and users, from linters to test runners to an entire build system. So obviously, we're going to talk a lot about tools, but tools aren't used in isolation. Tools are used on things, and they're used to accomplish certain tasks. So we really can't talk about tools in isolation either. We have to talk about all three, tools, tasks, and things. Things like web components, obviously, and Polymer, but also progressive web apps, service workers, testing, the purple pattern, bundling, offline, ES6 compilation, HTTP2 push, and something that's maybe the hardest task for some developers sometimes is just getting started. So that was a lot of concepts and words I just flashed up on the screen. And that's not even the complete list. So what does all this mean that we have all these capabilities and responsibilities to deal with? Well, on the positive side, it means that the web is now a capable application platform. We can build amazing and truly native app-like user experiences. But all these capabilities also means that there's a lot of complexity to manage, and this can be pretty challenging. So this is where tools can help. Tools can help make this manageable, and they can help guide you towards harnessing these features effectively and easily. And so on the Polymer tools team, we develop our tools with a few goals in mind. First, we need to meet the unique needs of web components, users, and developers. And when I say that, I do mean web components developers, not just Polymer developers. Our tools are designed to work with all web components. So these needs include things like linting in a world of extensible HTML, documenting all of an element's API surface like attributes and events and styling, or bundling HTML together for production. Then we want to take advantage of the new features the web platform has to help deliver a great user experience and a great developer experience. Features like service workers, native imports, HTTP2, the template tag, and so on. Next, we want to enable and automate the best practices that the community and our teams have been putting together to develop and deploy great apps. Our tool should guide you down the best path by default. And finally, we want to make all of this extremely easy. Our goal is to make our tools as powerful as necessary, but as easy to use as possible. Anyone should be able to create a fast loading HTTP2 pushing service worker enabled offline capable progressive web app with web components that delights their users with a few simple commands. And that's where Polymer tools come in. So now let's do a quick overview of the tools we maintain and how they relate to each other and some of the tasks that you need to handle as a web developer. Here's a little diagram I drew of how our tools are put together. Our tools are organized around a set of core libraries that are task-specific, things like initialization, linting, local serving, building. Empowering many of these libraries is a static analysis system built specifically for the web called the Polymer Analyzer. You'll notice that these tools are laid out here roughly chronologically in the order that you're going to need them during development. And this matches your development cycle from getting started into your editing and running and testing cycle and then through building for production. So we can take a tour of these functions and the tasks they handle going through the development process step by step. Let's start with the Polymer CLI since most of our libraries are integrated and brought to you via the CLI. The CLI is our multi-tool for web components and progressive web apps. It has a number of commands to help you through all phases of development. And we put this tool together and we put everything together into one tool to make these commands easy to discover and easy to use together. And I'm very, very happy to say today that we released version 1.0 of the Polymer CLI earlier this week after a year of development. This was a lot of hard work put in by a great team of people, and we think it really helps web developers. Thanks. OK, so the CLI can be installed from NPM by running npm install dash g polymer dash CLI. And this gives you the global Polymer command. From there, you can run Polymer Help and get a list of all the commands that are supported. So I'm going to go through the most important of these commands. But before I do that, I want to go over a couple of key concepts that we're going to refer back to as we go along. Things like the Polymer Analyzer that I mentioned, the AppShell architecture, and the purple pattern. The first is the Analyzer. So I mentioned earlier that our core libraries are powered by the Polymer Analyzer. The Analyzer is an engine that can parse and understand HTML, JavaScript, and CSS. And it follows the imports between these files to determine your entire project's dependency graph. And by doing that, it knows exactly what you use and where you use it. We then use this information for linting, building, generating docs, and so on. Next is AppShell architecture. So this is a pattern for structuring single-page apps so that they're well-organized, fast, and can be loaded incrementally. Within our tools, we break this down into three special types of files. The first is a single entry point, which is the initial file that's loaded by the browser. The entry point bootstraps your application. And in client-side, routed applications, it's typically loaded from many different URLs, maybe slash home, slash profile, a blog post URL. And it has to be very, very small because this file might not be cached well because it's served from all these different URLs. The entry point then loads the shell. And the shell contains your common application logic, maybe shared UI components like headers, and footers, and menus, but also the router. And when the shell loads, the router boots up, looks at the URL, and decides what view to load. And then we have fragments. Fragments are usually these lazily loaded views, but they can also be other lazily loaded components or libraries. Together, these pieces let you build an app where you load just the files needed to render the page the user is currently looking at. And last, we have the purple pattern. This is a pattern that the Polymer team developed as a way to describe how to serve and render lightning fast progressive web apps. Purple stands for push your critical resources, render your initial route, then in the background pre-cache the remaining routes. And finally, as the user navigates your app, lazily load the routes they go to on demand, either from the cache or from the network. And if we put all of these concepts together, we can look in an example app structure. So here we have the app shell up at top, which lazily imports a couple of views, which then import the rest of their dependencies. And some of our tools need to understand a little bit about this structure. So we let you describe it pretty simply in a file we call polymer.json. And we have fields for the entry point, for the shell, and for the fragments. And then when we talk about purple loading, what we mean is that when a user visits a particular view, you want to push all of the resources that view depends on. These ones highlighted in pink here. And that way they load as fast as possible. And then in the background, you want to pre-cache the remaining views and their dependencies. OK, so that's the end of the key concepts. Now let's get into the actual tools. And let's start off with the init command. I'm going to go to a little demo here and pray to the demo gods. OK, so switch to my computer. All right, hello. All right, so I'm going to start off with an empty directory here. And we're going to initialize a new application with the Polymer init command. So you see we get a menu here of different kinds of templates. We have the element template, the application template, Polymer starter kit, and a template called shop. We're going to choose the Polymer 2 starter kit, which is a really good starting point. That's why it's named starter kit. And you probably didn't see there quick, but it created some files. And now it's installing all the dependencies with Bower. And this is somehow working over the network, so I'm really excited. All right, so there you go. It installed everything. And we can look at the, yeah, yay for the network. So we can look at the files on disk here. It's kind of hard to navigate there, so we'll open it up in my editor. And we can see that it gave us, let's see, a source directory. This looks like we have some elements. It gave us an index.html. This is the entry point I was talking about. It just sets up some metadata, and we have our polyfills and imports the app, creates the app. You can see we also create a manifest for you. This is required for progressive web apps. So by default, we're creating a progressive web app for you. We even have a service worker down here. And then as I mentioned, the Polymer.json file, where we describe our fragments. So looking at the files on disk is mildly interesting, but what you probably care about is looking at how this template actually looks in a browser. So we're gonna use our local dev server with the Polymer serve command. And that's gonna start up a server for us. We're gonna take this URL here and copy it into our location bar. And here we have an app. So this is the Polymer starter kit. You can see it has a number of different views. These are lazily loaded views, so when I click on them, they're being fetched over the network. And if we open up dev tools, we can see here that the app is progressive. That's nice. The menu turns into a drawer. And we can go into the elements panel and introspect what the app created for DOM when it booted up. So here's the main application element. So one nice thing about web components being native to the platform is that Chrome's dev tools now becomes a web component developer tool. Because Chrome understands web components, the dev tools understand it too. So we can see here, if we drill into the application element, that we can see the shadow root. So now we can see how the internals of our custom elements are put together and how they compose. And we can also interact with these elements in the JavaScript console. So $0 would get us the last element and print it on the console. We can see that this element, it's not just a unknown HTM element or a div or something like that. If we look at the constructor, we can see that it's actually the custom element class defined in our application. And because this element, oops, there we go. Because this element has an API that actually lives on its DOM node, we can interact with it. So notice how we can change pages here. We go to view one and view two. That's driven off of property on the element. And we can go ahead and set that to view two. And when I hit enter here, this is actually gonna change pages and change to view two. So dev tools now gives you a way to interact with the structure of your app as custom elements. It can be very powerful. All right, so that's a knit. And let's go back to the slides. There we go. So that's a knit. You saw the built-in templates that it has, but we also allow you to install third-party templates. So this is based on the Yeoman Generator and any MPN package that starts with Generator, Polymer, and Knit that's installed locally will show up in that menu there. And we have some really nice templates that are available on MPM. One that has a custom build, so you can use Gulp. Another that doesn't even use Polymer just shows you how to use vanilla web components. One that shows you how to use Redux with Polymer. And another one that shows you how to use the Vadin elements. And then you can, of course, publish your own. All right, next, let's move on to Linting. So Linting is done with the Polymer lint command. And I have another demo for you if we switch back over to my laptop. There we go, awesome, thanks. So we can run the Polymer lint command and we can see here that, well, we didn't ship you a template that has Lint errors. So that's good. Yeah, yay. So we can go ahead and make some errors and see what the Linter does. But seeing Lint warnings on the command line isn't that exciting. What I'd rather do is show you how we've integrated the Linter and the Polymer analyzer into the IDE with our set of IDE plugins. So here I've opened up the project and I'm gonna go into one of the elements that we have. This is the main application element. And so we can look around and one of the features that our IDE plugin has is hover over documentation, not only for JavaScript, but for HTML. So here I'm hovering over DOM module, which is a built-in element. And we can see we have nicely formatted docs with syntax highlighted samples. So that's DOM module. We can come down to the main template for the app and see if we can look at app header layout and get the documentation for that. So that's really, really useful when you're using elements for the first time. And so one thing you might wanna do when you're editing is use a new element. So we're gonna use one of our favorite elements here, paper button. And hopefully you can see that on the screen, but we have a green squiggly for a warning there and it tells us the element paper button is not defined. And the plugin knows this because we haven't imported that element yet. So we need to go fix that. We're gonna go up to the top of the file here where our imports are. And I'm just gonna copy this paper icon button import and change it to paper button. All right. Oh, but you see we have a warning here too. This says unable to find the file because I didn't remove this last paper icon button. So let's do that. And hopefully you notice how fast that was. The Polymer analyzer and the ID plugins are built to incrementally analyze your files. So we're not reanalyzing the whole project here, just the file we edited. And now if we go down to where paper button was used, we can see that there's no warning. And if we hover over it, we get some documentation. So this will tell you how to use the element and we can see that there are some attributes here. So we might wanna use one of those attributes like raised. All right, so if we start typing raised, we see that we get documentation for the attributes including the data type. And if we hit return, we get the code completion for that too. So this is a really nice productivity booster for when you're writing HTML templates. Okay, so you're using this new element paper button. You might be curious about how paper button works. And so we've also added jump to definition. If you hit F12, we're gonna be brought into the paper button definition here. This is actually in our third party dependency folder. And this even works for attributes. So if I highlight raised and hit F12, we're gonna go to the definition of that attribute within the element. So this brings a really advanced level of ID integration even to HTML template editing. And this also works with all web components, not just Polymer, if you use JS doc and your web components, they too will show up in your IDE this way. Okay, and that's the IDE demo. Back to the slides. All right, so you saw some of the features there. We lent HTML and JavaScript, produce errors and warnings. We have hover over documentation, code completion and jump to definition. For the warnings that we produce, we have undefined elements, properties and attributes. We'll tell you about invit valid binding syntax and invalid HTML imports. We also have rule sets, which we've introduced recently. So we have a Polymer one rule set and we have a Polymer two rule set. We also have added this Polymer two hybrid rule set. So in the Polymer team, we tried to make it very, very easy to upgrade from Polymer one to Polymer two. And as part of making that transition easy, we invented this thing called hybrid elements. And these are elements that work in either Polymer one or Polymer two. And the way they do this is by using that subset of features, which is available in both versions. So this Linter rule set right here will warn if you're using anything that's only available in Polymer one or vice versa. So this should be a big help when you're upgrading from Polymer one to Polymer two. The Polymer ID plugins are right now available for VS code and Adam. And you can install them via the normal methods for installing extensions. And that does it for the Linter. Okay, next move, let's move on to serving. So we have a development server built into the CLI. We do this because when you're developing from a local file system, loading things from the file URL hits a lot of security roadblocks. So most people need at least a static file server. And we added a couple of conveniences on top of that. The first is for reusable components. So HTML imports work a lot like ES6 modules where you have to import by path. And the way we do this is we import by relative path that reaches up out of your package and down into a sibling package that's your dependency. And so if you have a URL that reaches up out of your package that might not actually exist. So what we do in PolyServe is we remap everything to look like they're siblings so that you can access your dependencies without having to do a build step. The other convenience we add for applications. So if you have a client-side routed application with nice URLs, you might have some URL with a long path that the client can handle but doesn't actually exist on the server. So rather than sending a 404, what we do is we send back that entry point file that you set up in your Polymer JSON. And it gives that file a chance to boot up, load the router, and handle the URL, and try to render that URL if it can. And finally, we've added auto ES6 compilation. So all browsers these days, all the current Evergreen browsers, support ES6. But we also support some older browsers that don't in turn support ES6 like IE11. So what we do is sniff the UA string on the server and if we detect that your browser does not support ES6, we'll automatically on the fly compile all of your JavaScript using a very standard preset to ES5. And if this isn't working for you for some reason, Chrome DevTools can do some funky things and pretend to use another browser, be another browser. You can always set a flag to either use the auto compilation or always compile or never compile. All right, so that's the server. Next, let's move on to testing. Everybody should be testing. So we built a test framework into the CLI. And you run this by running Polymer test. And this uses one of the oldest tools we have on the Polymer tools team, Web Component Tester. And this is a series of helpers that are designed to make testing web components more convenient. So we bundle in popular libraries like Mocha, Chai, Lowdash, and a few others just to make it convenient to write tests. And then we also have this concept in Web Component Tester of HTML test suites. So because custom elements live in the DOM, they're often created by actually being in the markup of your application. And so it's very natural to write tests where you want to write actual markup to test how your element operates. And so we have HTML test suites. And then on top of that, we've added a declarative test fixture helper called, appropriately enough, Test Fixture. So here we can look at what an example test suite looks like. And you can see that at the top, we set up our environment. We import Web Component Tester, and we import the element that we want to test. And then next, we have a test fixture. And this has a template with an element in it. And here we want to test that an attribute sets a property in that element, so our markup contains the attribute. And then when we run our test logic, we call this fixture function, which looks up the fixture by IDs, stamps it to the document, and then returns your reference to the content of that fixture. And then finally, you can run whatever test logic you need. Here, we're testing that an attribute set on the element actually reflects to a property. So this makes it very, very easy to write Web Component Tests. You can launch it with just Polymer Test, which will try to find every browser on your system and run it in all the browsers. Or you can choose one browser with a dash L option. And this will get the test runner started. Launch a browser window, which combines all the results of all your test suites into one window, and then gives you a fairly standard report where, yay, all your tests pass. And hopefully, you have more than two, which I have here. OK, so Web Component Tester is actually built on the same web server as Polymer Serve. So this means you get those same conveniences, like packages being mapped to siblings, or the entry point fallback routing for applications, and auto ES6 compilation so that it's easy to test on IE11. OK, now let's get to building for production. This is the largest or most complex part of the CLI that we offer. And you do this by running the Polymer Build command. But before we get into the details of Build, I want to talk about some principles that we have in the Build system. The overarching principle here is that Builds are optional optimizations on your project. We want your project to work as it is as source on disk. And this is so that you can have a really fast edit refresh cycle without building. So one reason why we do this is because we want to get out of the way. We want this to be fast. But another reason is because you might be using other tools that require building in order to run your project, something like a compiler, like Babel, or TypeScript, or maybe a SAS, or some other processor. So we don't want to have any kind of conflict there, so you'll be able to use those. And our tools don't have to run. Also something that's kind of unique with our tools is that we don't build based on the file locations on your system. We don't build based on globs or some complex configuration. We build based on the dependency graph. So once we're able to find the entry point of your application, we can find all the files that we need to process in the Build. And our Build system has a bunch of built-in optimizations. We want these to be easy to use best practices for progressive web apps. So we have minification, bundling, compilation, and then fancier stuff like service worker generation or push manifest generation. And our Build system forms a pipeline, somewhat like what you would use in Gulp. In fact, we let you use our Build system from Gulp. And the way it works is that the files in your project come through into the analyzer. And that actually discovers all of the files that are part of your project. It then splits them out into your first-party sources and your third-party dependencies, just in case you want to process those differently. And then it feeds them into an HTML splitter. We like to write scripts and styles inline on the Polymer team. So this lets us extract those out into separate files so that they can be processed per file type. So then we go into those per file type optimizations. We rejoin the split files that we created. And then finally, we feed them to global optimizations like bundling or service worker generation. And we control all of this with the Build section of our Polymer JSON file. Here, you're able to specify one to many different builds that you can create while you run the build command. And we have options for all of the different optimizations here. And they're pretty simple. You can just turn them on and off. And next up, I'm going to run through all these options and show you what they do. But while I do that, I want to show you why they matter. Why is it important to run these optimizations? What do they do for you? And in order to do that, we're going to measure. We're going to measure how long it takes to load an example application after we've applied these different optimizations. And of course, for that, we need a test subject. And so we're going to use the shop example application. This was a demo built by the Polymer team. In order to show off and prove out these best practices that we determined are good for PWAs, shop makes a great example because it's a full-fledged e-commerce site. It has a home page, it has product listing pages, product detail pages. It even has a shopping cart in a complete checkout flow. It pretty much does everything but actually ship you the shirt. And so we're going to measure things in Chrome DevTools. And I want to mention something that's really, really important if you're using DevTools for performance measurements. And that's that your development workstation, even your notebook, is likely a lot more powerful and has a lot better internet connection than your users. Most applications these days, their users are possibly mostly coming from mobile devices and are on pretty bad networks. So it's really, really important that if you're going to measure in DevTools, that you turn on network and CPU throttling. So here I've chosen the regular 3G profile, which gives us 100 milliseconds latency and 750 kilobit per second download speed. And then very, very importantly, I turned on CPU throttling, 5x slowdown here. And this is important because your mobile devices just can't parse and execute JavaScript nearly as fast as your laptop or desktop. So before we start diving into numbers, we need a comparison point here. So I took the shop application, and I actually de-optimized it a little bit. And I made it so that it eagerly imports everything. It's no longer lazily importing the views. So this is kind of like a naive structure for building an app. And it doesn't have any minification, and it doesn't have a service worker. So I ran this through DevTools, and I got some numbers. We have 5.9 seconds for first meaningful paint on the initial visit, and 4 seconds for first meaningful paint on a reload. So we're going to apply some optimizations one by one and see how well they do against this baseline. And the first one we're going to do is minification, because pretty much every app does minification. You would be kind of crazy to go to production without doing it. So this is kind of like the standard optimization. It's great because it makes all files smaller. And in our system that splits apart HTML, we also minify inline scripts and styles too. And so the theme I'm going to go with here is that we try to make all of these optimizations as easy as possible to apply. And so we have a couple of options that you can add to your Polymer JSON, which will turn minification on for the different languages. And if we turn minification on, we can see that we've brought our initial load first paint time down to 4.4 seconds, which is 25% better than what we started with. So this is a no-brainer. That's a big advantage, and we should definitely do that. OK, next we're going to look at an optimization we call insert prefetch links. This adds link rel equals prefetch tags to all the entry points and fragments in your application. And the benefit of this is that when you're about to load a view, and it has a bunch of dependencies that need to be loaded, this tells the browser up front the entire list of what it's going to load, so it can download them all in parallel. This reduces a lot of round trips that would be required to load a resource, parse it, find an import, load another resource, and so on. And so you can get some of the benefits of HTTP2 push by doing this, not all of them, but some. So this is good for environments where maybe your server doesn't support it. And again, this is very easy to apply. You just turn the insert prefetch links option up to true, and then we can measure the results. And you see we've gone from 4.4 seconds down to 3.1 seconds. This is another 30% improvement, so this is a great optimization that's easy to apply to your project. Next, let's look at service worker prefetch. So service workers are a really, really powerful tool. They're essentially a background worker, a network proxy, and a programmable cache. But with all that power comes very little guidance and structure on how to use them. Coding one of these by hand would be very complicated and tedious, so we auto-generate one for you. And the one we generate does two things. First, it pre-caches all the dependencies of your application in the background, so you get very fast transitions when your users navigate. And second, it makes your application automatically offline capable. And the way we do that is we do the same entry point fallback routing that we do on our dev server. If the user goes to a URL while they're offline, that's not in the cache. Instead of sending a 404 as a response, the service worker will send the entry point, which allows the router to boot up and to handle that URL and render the view you want to. Again, we want to make this as easy as possible, so you just put add service worker true in your Polymer JSON. And if you do that, you see that, well, our first meaningful paint number didn't really change. It went from 3.1 to 3.2. That's basically in the noise. It's the same. But look at what happened to the first meaningful paint repeat visit. That went from 2.6 seconds down to 0.9 seconds, which is a 65% improvement there. That's huge. And this is a benefit for some of your most important customers. These are your repeat visits or your signed-in users, people who like your app, people who maybe have installed it to the home screen and are tapping on the icon expecting a native app-like fast load experience. Maybe faster than native app, actually, sometimes. OK. Next, let's take a look at lazy loading. Lazy loading is not so much of a tool but a technique. But I want to talk about it here because of how much work we put in our tools to support lazy loading. And the idea with lazy loading is that you only import what the current view needs to render. And you import everything else on demand as you need them. And in Polymer, we have a couple APIs to do this. One is the import href function, which adds a new HTML import to your document. And the other is a thing we've released recently called lazy imports, where you can declaratively describe the lazy structure of your application. And if we apply this to our other optimizations, you see that we've now brought down the first meaningful paint number to 1.9 seconds. And this is another 40% improvement over previous. And the first meaningful paint on the repeat visit stays there at 0.9, lightning fast. So that's good. And finally, I want to talk about bundling. So here's where we've taken care to play well with lazy imports. We do smart bundling. So bundling normally merges all your dependencies into one bundle. And then the more advanced tools like us and Webpack will actually bundle things into multiple bundles if they can detect the lazy structure of your application. So our bundler is lazy import aware. It generates multiple bundles depending on this lazy structure of your application. And the way it works is by analyzing the dependency graph here. So what we do is we look for every file, what is the combination of entry points that require that file to load. And then based on the unique set of entry points that we discover, each one of those becomes a bundle. And so now we can get a very fine-grained bundling that works well with your lazy import structure, no matter what you're lazy importing. The only problem with this is that it's possible to create too many bundles and have a negative impact on your performance. So what we've added on top of this is this idea called bundle strategies. And a strategy takes a bundle manifest, and it modifies it and returns a new bundle manifest. And the one we've included in the CLI has a heuristic where it says, if any entry point is required by more than, say, two, or any bundle is required by more than, say, two entry points. It combines those all into a shared bundle. So you get one per view plus one shared bundle. And this ends up being a pretty good option. And we also make this incredibly easy to use by just simply setting the bundle property to true in your Polymer JSON. And if we apply this on top of all your other optimizations, we get our first meaningful paint number down to 1.5 seconds, which is really fast on the 3G network with a slow CPU. And we still keep that blazing fast 0.9 second repeat visit time. So altogether, we've had a 75% reduction in first meaningful paint time, or four times faster, all for just setting a bunch of options to true. OK, so next, I want to talk about compilation. It's not really an optimization, but it's required for older browsers. So with custom elements, we have this interesting situation where custom elements have to be ES6 classes. But we support some browsers that don't support ES6. So on one hand, they have to be ES6. And on the other hand, they can't be ES6. And let me show you why this is true. When you write a custom element, you extend HTML element. This is a built-in class very similar to array or map. And when you extend a built-in element, you have to have a real constructor with a real super call so that the system can initialize that built-in object properly. And when you compile a constructor to ES5, you don't end up with a super call. You end up with something like this where you're calling the constructor, the super constructor, like a function. And if you try to do this in a browser that actually has that built-in, it'll throw an error. So again, we have this thing where custom elements have to be ES6, but you can't run ES6 on IE11. So we try to take care of this for you in your tools, in our tools. And what we do is we recommend that everybody write their elements and distribute their elements to Bauer and MPM or whatever as ES6. And then you only compile them, if necessary, at the application level. And this is because the app is the place where you know what browsers you need to support, what environment capabilities you're targeting. And so the app is the thing that knows what compilation needs to happen. And the Polymer Tools help you do this because since we're based on the dependency graph of your project, we can compile all the JavaScript that's reachable from your application. So you can create two builds, one that's ES5, one that's ES6. And if you have a smart server, you can serve ES6 to all the modern browsers and ES5 to browsers like IE11. But not everybody has a smart server, so we've made it possible to produce universal builds that you might deploy to a static file server like GitHub Pages. And we do this with something we call the ES5 adapter, which patches up the custom elements environment on browsers that natively support it and forces them to kind of accept ES5 subclasses of HTML element. And again, we want to make this extremely easy to do, so all you have to do in your build configuration is set compile to true for JavaScript. OK, so that's what build does. It does minification, bundling, compilation, all this stuff. And even though we've tried to make each individual item here as easy as possible to turn on or off, it's still a lot of things to understand, keep in your head, and to configure. So we want to make this even easier. And so recently, we introduced build presets. And we include what we think are the three most common and useful presets for you to use. We have ES5 bundled for your older browsers or for the universal build. We have ES6 bundled for newer browsers, but maybe your network or your server doesn't support HTTP to push. And we have ES6 unbundled for that full purple incremental serving. And so this is what happens to your Polymer JSON file when you use presets. You can go from specifying each one of these individually to just specifying the preset. And again, the theme here is we want to make this as easy as possible. It shouldn't be difficult to build an incredibly fast app even for emerging markets. OK, so that takes us through our whole development cycle from getting started all the way through building for production. Or does it? You can't just build for production, right? You have to actually put your app into production. And so we're adding a new step to the tool chain now, which is targeted at deploying your app. And so now I'm happy to announce that we have a new initiative that we're calling Purple in a Box. And this is a series of purple reference servers. They're smart servers that can do differential serving and serve ES6 to the browsers that support it, ES5 to those that don't, and use HTTP to push depending on whether the browser supports it. We're building out configuration that's generated based on the CLI and your dependency graph for these servers. And we have initial versions for Node that works well on App Engine and also for Firebase. The Node version is the first one we're releasing. It's in a preview state, but you can look at it now. This works for Node and App Engine. And you can look at it at the GitHub organization Polymer and purple-server-node. And you can also install it right now on NPM, just NPM install purple-server. So this server is CDN and edge cache friendly. So even though it serves different resources to different browsers, they all exist at different URLs so they can be cached aggressively at the edge cache. And it also is designed to work with HTTP to push proxies. This is what App Engine does, where it takes an HTTP one server. And if you specify certain headers in your response, it'll automatically turn those into HTTP to push requests. All right, so now that really brings us to covering the complete development cycle. All the way from getting started through editing and testing through production. And that's basically my talk. And if there's any one thing I want you to take away from this talk is that Polymer tools make it really easy to start, develop, and build with web components and Polymer so that you end up with a fast, purple-enabled, offline capable, progressive web app. And that does it for me. Thank you, everyone, for coming out.