 Hello. Oh, that's quite loud. So confusingly, I'm the second Australian Sam you're seeing today, so we do look a bit different. So you'll be able to tell us a part later on. Today, I'm here to talk to you about ES6 modules, how they work, what are the ups and downsides, and how they integrate with existing code. And this talk is definitely a technical deep dive, which is basically code for there'll be a lot of stuff on the slides, so pay attention. As a bit of a preamble, I want to give some naming stuff. So I just want to say that ES6 modules are also known as JavaScript modules or ECMAScript modules if you've heard of them before. They're not common JS modules, which are probably the most common, non-standard approach to modules we have today. And it's what we get if you download nearly any module from NPM or Yarn. There is an interrupt story here. ES6 modules don't have to exist in a vacuum, and I will get to that later on. So this talk is pretty big. It's comprised of three parts. The first part is, I'm going to give a brief intro to ES6 modules, what they do, and how they work. And at the end of part one, you could walk out. Please don't. But you'd have an understanding of the how and why, including the story for older browsers. And part two, modules are really great. I think they're quite cool. It's given me a whole new lease on writing JavaScript. But they also change a lot of subtle things about the way we write it, which is why I split this part out. And finally, I'll talk a little bit about integration with existing packages and code splitting. So maybe most importantly, why do we care? Well, obviously we care because Polymer's moving to ES6 modules, and that's super important for us using Polymer. But the other reasons are, any language really needs modules. Anything more than a trivial program in any language needs some sort of include system. And of course, in languages like Python or C++, we've obviously had that since the inception. But for JavaScript, we've not had this. You include a script tag in your page, and just hope it works. And we all know this, but including loosely defined script tags like this is such an anti-pattern, right? Yet we see it really commonly. And secondly, we want to use the platform. We obviously have great community-driven approaches. I mentioned CommonJS, which I'll come back to. But they are never picked up as a standard and never really adopted by browsers. And this is for a couple of reasons. But what I think is really interesting is that the ECMAScript standard for modules has a really intentionally small specification. It's actually really simple when you get down to it. But it has obviously a few nuances, which I'll be talking about in a little bit. So this is technically an example. This is how you use an ES6 module. You can either include a script file to run as a module, which is the code snippet at the top. Or I can write a module script inline, which is what you see at the bottom. You know, okay, this is great. I've specified type equals module. But what does this actually get me? Well, it gets you a couple of things. Firstly, it gives you superpowers. Don't worry, that's the worst slide transition we'll have the whole time. But it gives you superpowers, JavaScript superpowers, to import and export code. Sadly, modules don't let you fly. I hear that's reserved for Python. Oh, and there's some joke there. Okay, no Python programmers here. So now you can import other code. And here, you know, I'm declaring my first script tag as a module. So I can request that this symbol answer, which comes from the bottom, appears from the, it's in the question source file. And I can request that that will show up in my index HTML. And now I can use it in any other way, any way that I'm used to. You know, this is really the basic core of modules. And I'll point out that in regular JS, these symbols are there, right? You can't use them. So you can't just include these files as regular JS and mix and match that code. So here's a slightly more detailed example. Unlike the last slide, we're now including the JS file via the source attribute. You know, the point is here that it doesn't really matter whether the script is inline to your HTML page or you're including it, they still work the same way. And because we started with type equals module, both these files are kind of run in what we call module mode. You know, even though the bottom one purely has side effects, I'd purely export something, sorry. It has the superpower to include additional modules if it wanted to. So what we've basically built is sort of like a path of imports. You know, this is our little example. We've got a HTML page, it pulls in code and it pulls in question. But you could add, you know, another dependency here. Or I could add something else that's dependent on my multiple files. I can also have other HTML pages which maybe enter into this graph of dependencies that I have here, which is super interesting and I'll refer to this a little bit later on. This slide details some of the options. So we saw the really simple mode of exporting and importing before. I find there's actually a lot of resources about the format of this kind of important export syntax online. And it's interesting, but it's really well covered. If you Google for things like ES6 modules, most of the articles you'll find will talk about the formats of these things. So, you know, it's really well covered. I'm gonna mention a few things. You can see on the left here that we have, sorry, on the right, we have exports for cat and dog. And on the right, yes, we import those things as sort of named things in this other file. There's a few nuances. You can also have a default export. Functionally, while it looks different than the other types of exports, it actually is just an export with a name default. We can't use it because it's a keyword. So we can't export something named default. It's has to be special. Then you can see this on the right. If you actually use this format on the very bottom right, which talks about importing the whole module as an object that looks like a dictionary, you'll actually see that default just ends up being a key of that dictionary. So that's kind of interesting. The most important part of these statements is that they have to appear at the top level of a file. And I'll visit that in a second. So let's talk about what I'm allowed to export. So I can export something that's generated. Since apples are in season versus bananas, I'm going to return a different function. And so this file will export an unknown method that's not known at sort of static build time or whatever as the fruit property. But I can't optionally export something. This doesn't appear at the top level. So this is basically a syntax error. And on the import side, there's kind of similar problems. The rules around this are basically saying that I can't import from an unqualified path. It has to be a relative path with a dot at the start or a full URL. You can't import not at the top level. So even though this try catch block isn't actually a control structure, it doesn't actually optionally run, this is an error because it's not appearing at the very top level of a file. And at the very bottom, you see that you can't import a dynamically generated script either. So think of these restrictions as like, what can a very dumb static parser pass without running any code? And this matches traditional languages like Java or Python or CLC++. And it comes from that small specification that I kind of mentioned before. So this is the very basic intro to ES modules, but of course, what's the browser support? And so what I've shown you so far isn't theoretical, right? You can use this in all major browsers, but I'll admit as I give this talk, I think five or an edge is still behind these, behind flags, we saw that in the keynote. But if you're watching this sometime in the future, well, as an Australian would say, she'll be right. I'll also note that when I say Chrome, of course I really mean Chromium and browsers derived from that. And modules are implemented at the underlying V8 level. So it's possible for support to flow from there. So okay, we're through a sort of part one, you're convinced, right? Modules are great, syntax looks good, and you're gonna migrate all your code to this format. Well, we're web developers, so we know that nothing comes for free. So let's talk about the support story. And I'll start with our favorite thing, old browsers. Browsers will basically ignore script tags with a type they don't understand. So you can safely tell them, hey, here's this module script, and actually, your IE11s or whatever, will actually just ignore this completely. It's how people have sent coffee script or dart down to the browser for years now. But of course, if we write stuff in a module format, we still need to transpile or compile for browsers that don't support ES modules. You might already be doing this, right? Polymers build and serve commands with the CLI stuff, already run a transpile step for you for these browsers. And so yes, this does mean we need to ship two sets of code down to pretty much all our users. And when we do the transpile, we obviously need to include both those sets of output. So the way we do this is we basically do what's called a no-module tag. So we add this script tag that browsers that support ES6 modules will actually ignore with a small caveat. So putting it together, you'll basically, on your eventual HTML page, you'll have two script tags, one for module browsers and one without. And this is great, right? Because you don't have to run script to generate the right imports, new browsers get new and old browsers will get old. But that start from before, actually Safari 10.1 actually shipped without no-module. So there's a small bugging to work around. We've actually got a solution. There's a small JS snippet you can include that will stop Safari from loading this no-module code. This is through a fixed and future versions and everyone else is shipping with this feature. It will eventually go away, right? All browser versions will eventually disappear like tears in the rain. And the biggest cost maybe in the very long term is that users on this very specific version might end up downloading both your bundles of code. So I've mentioned a bunch of stuff. I've said, well, you need to transpile, you need to ship two code paths and you need to deal with a fun bug. So why should I even bother with ES6 modules at all? And there's basically two really huge compelling reasons. The first is this immediate dev cycle. When you're building modular code and you have a native browser that supports it, you can, oh, sorry, if you don't have native browser support, you know, you've got to build or run or whatever to actually see your output. And even if that happens automatically, you know, on when you save, it's still a cost. It's still got to run something. Instead, with ES6 modules, you can literally save a file, go to your browser, refresh it, and your code is just done, right? It reads all the stuff in a modern way. The vastly more important thing in my mind, actually, is that it's a high watermark. What this basically means is that ECMAScript modules are a 2017 feature. Literally any browser that supports ECMAScript modules supports a ton of features that you may have been afraid to use or afraid. You have to polyfill or afraid you have to work around. So if you have a separate serving path, you can quite literally only serve code to these browsers that you know that you can be confident about that's gonna work. We get to restart JavaScript development from this point. And here's a table I sort of worked on earlier. This is a kind of a laundry list of features and you might have been afraid of shipping this or using these. And of course, if you have an ES5 client, you've still got to transpile. You've still got to ship that old code to support those old browsers. But if you're shipping ES6 code in parallel, you can basically drop the polyfills for all these features. You know, your reward uses on up-to-date browsers. And to pick on a certain feature, ASIC can await. To transpile this as a bunch of bloat, they've got to rewrite the code to be a weird generator thing. And for ES6 modules, you can actually safely ship this down for those modern browsers while still supporting that very long tail of browsers that obviously will never get support for this feature. And let me stress one point, right? We don't penalize old browsers for doing this. You're probably already transpiling these features away and including the right runtimes. It's just the idea that for modern browsers, you can ship an entirely new fast code path that doesn't have to deal with any of that legacy cruft. And okay, so what's the last part of the process? You know, I've mentioned transpiling, but I want to revisit this a little bit. You know, you want to convert ES6 code to ES5 code, and you might already have built tools and infrastructure that does this for you, right? They'll adapt nicely to ES6 modules if you just include them. These include Closure Compiler, which is by Google, of course, and Rollup is a good example of a starting point for something that will get rid of ES6 modules and ship it down to something that's a bit like ES5 for those older clients. And Rollup is basically what I recommend. It's an open-source tool that's built entirely for merging ES6 modules together to give you one output ES5 file. You know, and actually to finish that whole process and compile and transpile, you would use Rollup to merge your code, and then, at least for me, I would include something like the Babel plugin to transpile it to the version you're targeting. That's how you can build your ES6 modules down to ES5. And the gold file looks a bit like this. We've all seen something like this probably, but I want to focus on two main things. You know, we include the Babel plugin to actually do the transpilation for us, and we tell Rollup to give us an iffy out. And I'll come back to this later. But I have an opinion for simple apps. And for production, I actually suggest using Rollup with this flag and no plugins to ship a single file even to browsers that support ES6 modules. So this is interesting, right? Like, I'm telling you to use modules, then I'm telling you to roll them up when you actually finally ship this code. So firstly, you know, it's smaller. You know, you don't need to include polyfills, but I covered that before. But there's a few benefits, right? So when you use Rollup or tools like it, even to ship ES6 modules as a single file, you get a bunch of benefits. And one of the big things is called tree shaking. You know, when you merge modules together, you basically get rid of code that you don't need or isn't being used. And this isn't a talk about compilation. There's plenty of other resources out there on that. But the key is, you know, these will really save you a lot of space. And if you're including a lot of dependencies, which ES6 modules let you do, you know, you can have hundreds of tiny dependencies using different, you know, maybe it's written by you or written by someone else, you can be very confident in saying that the stuff you're not gonna use will get thrown away. And having a lot of module files is also gonna cause what's known as request chains. So if your browser fetches an index page, it's gonna then see it needs this file main.js. And then it's gonna see that it needs depth.js and so on and so forth. And before you know it, you might have done a whole four or five round trips to the server. So of course, you might have heard of HTTP to push. And this can solve this for you. You know, tell your clients, here are all the JS, here's all the JS files they need. You know, before they even ask, right? I can tell the client, you need all these five files. You know, have fun with that. But a lot of servers won't do this for you, right? And in my experience, well, that's a great feature. It's not widely adopted, right? So you can improve your user's experiences by having just a single file. And there's also the head-over-head, right? If you have a lot of 100-byte files, maybe the head is at 200 bytes each. So rolling up your output could be really important to save space. Okay, so that's a lot. But I wanna go into some of the nuances of the way JavaScript changes when you write module code. So module code is always executed in strict mode. Basically, this turns more mistakes into errors and prevents some JavaScript anti-patterns that have been possible for a long time. So Mozilla has the best documentation on this. So go look up on MDN. But basically, you can pretend that every file has this used strict pragma at the start of it. And module code is always effectively invoked in what we call an iffy, and immediately invoked function expression. So what that means is that symbols you define in your module won't automatically appear in global scope on Windows. And this is what it would look like in code, right? Your module stuff would basically appear in the middle. This is informational, all right? Your browser doesn't actually do this. But it's something to keep in mind if you write any tooling around ES6 modules. And this is what it would look like, right? When we add code into here, you can imagine the sort of errors you would get. You know, the width statement, for instance, is sort of considered harmful and it's disallowed. And using this variable Q without defining it is also an error. You know, we haven't got a var statement or a let or anything like that. On the other side, you get some benefits, right? This variable X won't define, won't pollute the global scope. It won't implicitly create a property on Windows, which is kind of not usually what you want. But we can still access things like, let's say we had this HTML element, obviously we can still access things in global scope as we are normally used to that way. So there's something else I want to talk about. You know, in early examples, we saw that a module is usually either an entry point which runs code like your application or it's a dependency. You know, that was the example of a file that just exported something. But we can kind of have a mix of both. I can import a file just for its side effects, just to run it. And what's interesting is that this fits the model of many single file JS libraries. This is talking about integrating with the existing code. It's a really simplistic way to pull dependencies that maybe aren't written as ES6 modules, which turns out is most code right now. And so here's a real world example I was working on recently. You know, I wanted to use a base64 library which isn't available as an ES6 module, but it was available as a single minified JavaScript file which, you know, again, is pretty common for small libraries or feature polyfills. So I could target it directly for import, you know, even though it has no export symbols and its side effects will still take place. You know, and again, this isn't the be all and end all of existing code, but it's an interesting step. Modules are also deferred. They actually aren't allowed to run synchronously. The reason for this is because if you have a lot of dependencies, your browser could be sitting there for a long time going to fetch all those files before your page can continue running. You can, however, specify async. And what this basically means is that fast loading or cached modules will run in any order as soon as they're downloaded. And this is the same as normal script tags and the way that they would work. Modules are imported only once. This is good to solve, although it's not a huge deal. It kind of avoids this problem, which no one really does anyway. No one accidentally includes multiple versions of the same code. But what's more important is because modules are kind of a graph or a tree structure, they're kind of like singletons, right? Two files that include the same dependency will actually get the same module back and it will only run once. So let's take a look at what that means. So let's say we bring in this default import from food.js, even using different paths. These all point to the same file. And no matter how the browser imports that file, because it resolves the URL, the module object will always be equal. And this last case, this is where it would differ. This is a bit of a red herring, but it might be something to watch out for. We know these files are the same, right? We know the query string doesn't matter. But to the browser, it's a different file, right? There's a different URL, so this is gonna be a different file and this is kind of almost a hack to make a module run twice if you want to. Module imports are like functions in that they're hoisted. So this code here, which you see on the right, will actually run fine, right? Exciting method is brought to the top of the file just like this. And so we have another example which has an interesting trade-off with that, right? So let's say we have a new emoji polyfill. We need this for our emoji library to run properly. What we've learned, though, is since the import is hoisted, this actually won't work, right? The polyfill won't be available for the emoji library. We can basically solve this if you have code like this by moving our inline.js into an import because now these import statements will still run in order. They'll just run at the top of the file. So we can use the side-effect benefit to make sure that this window thing is gonna be available for our next file that runs. And lastly, which is kind of cool, circular dependencies are actually allowed in JS modules. And this is kind of interesting because it's something that we haven't had in other module systems. They work mostly because of some of the properties I've talked about on the last few slides. The basic rule of thumb for modules that have circular dependencies is to not do any work at the top level. Don't call side-effects just import and export things. So let's see an example. So I'm gonna give you a classical CS exercise. I have a superclass. Let's say it's a vehicle. We wanna subclass this and we wanna give each vehicle an ID. Makes sense so far, right? I have a new file which has a class car. It has an engine, a driver, whatever. It doesn't really matter. But because it inherits from vehicle, it's also gonna get an ID. So let's say we wanna have a static method on a vehicle that returns a car. This is kind of a javaism or kind of a basic polymorphism kind of exercise, but you have a builder on a superclass that returns a subclass. And maybe as a user, I treat that as a vehicle. I don't care that it's a car. So we now have a circular dependency, right? Car descends from vehicle and vehicle itself can return us a new instance of car. And this is actually more or less fine. If I import either of these files, this will work as intended. So this is kind of a good example of where circular references will really help you. But there's one case that would break it. So you might have noticed the static kind of ID property. This is kind of a property of the vehicle singleton, if you can think of it that way. So if we end up using that property inside the car code, to maybe say try to test or a default car, well, this is actually gonna fail. And you might be able to see why, but let's look at it why. Let's look at it anyway. Because a vehicle is effectively hoisted while we're in this kind of crazy scope. Because it's exported in line. ID is not really available yet. So what ends up happening is that this constructor will fail with a reference error. ID effectively is not available to this class yet. There are a few crazy hacks around this, right? And what may actually work out to be possible is maybe there's some interesting patterns that people will work out or realize that will kind of leverage circular dependencies in a very interesting way to do some cool stuff. But for now, doing this sort of thing, actually using the code in line in this file is the sort of thing that will bite you. Okay, so I've talked about a lot of nuances. Let's talk a little bit about integration. And as I mentioned before, ES6 modules don't have to live in a vacuum. So let's talk about the elephant in the room. This is a bit forced, but using the existing ecosystem of node code. Firstly, there is a simple solution. We've talked about building modules using rollup and how modules obviously are very different. And while modules can help you get rid of the massive script tags, there's some cases when you can just include them anyway, right? And a really good example here is when we just want to include some dependencies for tests. Here I include Mocha and Chai just for things to run my tests. And these just have side effects. And remember that modules are always deferred, right? So I can also defer my script tags because I know that they don't need to run until my module is ready either. But I want to talk about a more concrete solution, what you can actually take away and use in your own projects. What is the story for code you've installed? This code that's inside node modules. This is stuff yet again that's likely in common JS format. It uses require, it uses module.exports. So firstly, I want to focus on this sort of stuff. What's the solution, right? It's actually really simple. We want to build a special ES6 module that actually uses the require keyword and then re-exports those symbols again in ES6. This is obviously magic, right? Because we know the require isn't supported by browsers. So how do we actually do this? And just to go through it a little bit more, I want to make this very clear. We have our app on the right here. This is our ES6 module thing. All our modules are in pink. And on the left, we have our node modules that we're really keen on using in our projects. So firstly, we create what's called the support.js file. This file will use require to pull in the node modules just like we're used to. So we add these links here, right? We then use rollup to generate a rolled up JS file in ES6 module format. And we'll see how in just a second. This file bridges our common JS code to be module code. And because we've now kind of transformed this old stuff into module code, we can now depend on it wherever, right? In this case, I'm pulling in left pad, which I'm sure is very important and I'm not judging, right? I really want you to remember this left-right split. It's really the best way to split up our dependencies, especially with this wealth of code we've had over the years. Yes, we're introducing a compile step, but it's really just for the stuff on the left, right? It's not really a huge burden. It only happens when we change our dependencies, which we presume is not going to be that often. So I've talked about the support file, but what does it actually look like? We can just require code in this file as normal. Of course, a reminder, we can't really ship this. It doesn't really work. But I immediately then re-export them as ES6 modules. So this indicates to rollup that these are the things we're interested in. And as you see in the example, we might only care about one sub-symbol as well, and that's fine. So this is a sample gulp file, but there's really two important parts here. What we want to do is we want to tell rollup about the common.js plugin. And this basically allows rollup to go and read those old-style formats, and in its build process, it will output you a file that understands them. We also want to re-export as an ES6 module. So this is the example I was kind of talking about before, right? We want to use rollup with its config and say, well, I don't want an iffy. I don't want the final output. I want something in the middle. So what it generates is something that looks like this, right? It looks like a normal ES6 module that we can depend on with all the require statements eaten up and taken away. So this is now just another dependency in our graph of modules that I can depend on either in prod or dev. There's one other case I want to cover, which is kind of like an anti-pattern. ES6 modules don't allow dynamic imports and exports, and so unsurprisingly, most of the tooling around that doesn't allow it either. So even though we're using the common.js plugin for rollup right now, rollup will complain horribly at this, right? It's a conditional it has to evaluate to decide what to include in this output. So it basically returns a file that just has this error in it. And so I'll discourage you from doing anything that's, if you're going to integrate with old code, avoid those patterns that we want to maintain that ES6 modules prescribes. Another option, of course, is if you are lucky enough to see ES6 modules actually in node code, and obviously we're going to see that at some point with Polymer, you can actually use Yarn's flat option to install them. And this is for a pretty simple reason, right? Flat was covered in some earlier talks, but unlike node's require statement, which sort of has a few paths, it sort of has a few paths that it looks at, import requires very sort of fixed relative paths. So that means that the code you download, which will be ES6 modules, kind of chooses the approach to finding files that it likes, right? And in Polymer's and other cases, you're going to see code that looks like this, right? It's always going to assume that its dependencies are going to be essentially up and, you know, one sibling across. So we've covered that option, and I want to go on a bit of a detour. So one thing I was thinking about a lot with ES6 modules is this kind of code splitting story. You know, you've got a lot of code, we want to make it one file, but that's not always what you want. So, yeah, of course, with ES6 modules, isn't that code already split, right? We've already got lots of dependencies. Let's say I've got an application with two entry points, right? These are basically analogous to our two HTML pages that exist on our site. They import, you know, they import this graph of dependencies to achieve their goals. This is a bit contrived. I just drew some lines and some boxes just to make a cute example, but you get the idea, right? These are all things you might depend on and other libraries that they further will depend on. If we were to do my approach from before and basically say, let's roll up everything into a single file, we get two giant bundles, right? This is kind of a common problem, I think, with bundling any kind of code. You know, I don't know what my files really need. We end up just shoving all the JS in the output file and letting the client deal with larger download size. You know, there's a ton of code duplication here. But what we can do is we can basically identify what's needed at each entry point and we can basically walk the tree to do that. So, the reason I'm telling you about this is because the module spec is so simple that it actually really trivial for you to do this yourself in your own build steps. You know, we mark these with colors and now we can see almost implicitly what are the different kind of bundles we need, right? Initially, we basically want to include the code for entry one and entry two. Those parts are really obvious, right? We know that those files are only needed by those two entry points, so we can roll them up together. But what do we do with the rest, right? I mean, if there were HTML imports, we could actually just group them all together and send them down as one big chunk. And that's great, right? It's a block of shared stuff that both sides can import. But with ES6 modules, and I am admittedly simplifying a little bit, we can't really do that because of this. Because the two modules might export the same property name, we can't at least naively roll them up together, right? Two, one module can't export the same symbol name twice. And so our entry points might use these methods in different ways. And you know, you can rewrite them. I'm sort of alluding to the art, the art. This is a simple option and something like Webpack will actually do this. But to think about a simple process, what we want to do is we actually want to create different entry points into these two bundles, right? The left is D and the left is effectively E, right? The F is a byproduct of E. So this is the minimum amount of code splitting you need to do to basically serve these two entry points. And you can imagine, as you get more and more code and more and more dependencies, this graph might get more complicated. And like I said, the reason I've mentioned this is, so I've actually, you know, in building this talk, I went and built this tool, right? It actually goes and finds the minimum spanning requirement to basically split up your code into modules that don't duplicate code. But in doing it, it actually wasn't overly hard, right? The module spec is incredibly simple and it's really just a simple path that goes over and looks at this code and runs some really basic graph theory on it. And this is one of the huge benefits of switching to this approach. You know, common JS and require statements can support a lot of things. They can support, you know, variable imports. They can be not at the top level. And actually that really open spec makes static analysis really hard. So check this out if you are curious. So this is basically the last technical slide. It's also worth mentioning dynamic import. And I know this was mentioned for Webpack as well because they use it as their boundary for splitting code. But I want to talk about the function. It's not really available yet. You know, browsers are still building it now. But it is coming soon. You know, it looks a bit like this. You pass it a path and it returns a promise. No real surprises there, right? Which is unlike our regular import statement which happens synchronously. Secondly, it's sort of polyfillable. You can actually use this today if you want to. There is one catch, though. So we can actually do this. But the browser has no way of knowing what my current file is. So I can actually import food.js, but I'd have to tell this polyfill function what is my current path. Because essentially there's no way for the browser to say what is the currently executing ES6 module file. This is actually subtly different than the way we do regular JavaScript. Regular JavaScript, non-modules, you can actually use the document.currentScript property to find out who I am. But inside the ES6 modules, that's not allowed. We're not allowed to know who we are. So you have to include some information about where is the file relative to me. So thanks for listening. We've covered lots of stuff. And admittedly, this is a bit of a grab bag of technical stuff. So I appreciate everyone paying attention. So what are some takeaways, right? Browsers love modules. They get along fine. You know, there's even the no-module keyword for all browsers. The way we have to write JS will change a little bit. It's pretty subtle, and it's mostly to do with the way we interact with files. But there are things that might bite you as you go along. And secondly, modules play nice with NPM modules via roll-up. But we obviously recommend Yarn for the flat package approach. And I'll leave you with some further reading. My colleague Jake, who managed to avoid giving a talk, is hiding somewhere. And you can go grab him on myself after the talk and find out more about modules. And that's it.