 Cool. So code splitting. Yeah, well, it's nice to be back first of all. I think that's the first thing to say, isn't it? It's been a while. It has been a while. Thank you for joining us, if you are joining us. If you're watching this after the fact then, it's nice to have you do that too, I guess. But nonetheless, we are back. We are going to do code splitting. You're doing the coding. I am. I'm going to be on the live chat, which again, if you're watching this after the live stream, I mean, the live chat's not going to be there. So what are you missing out on? I don't know. We're going to find out. So there's lots of people already saying hello. So hello back to you. Amazing. So it's nice to see you all there. Right, code splitting. Seriously, joking aside, what are we talking about today? All right, so I guess I should start with showing where we're going to start with. So I grabbed our old router example code, where we have a little website with four subsections that you can navigate to. And we have little nice transitions and all these things. And I updated it a little bit to use the new ES6 module goodness. OK, so these are like the import statements, right? Exactly. So that probably needs to be a little bit bigger, I would imagine. Yes, I should zoom into DevTools. Let's do it like this. There you go. I think our cap's locked. So if we're going to reload, we see that it's loading the view, the router, and the app. And if we navigate, it's going to load some additional code. So there's like lots of code being handled. We've already got the very first question, which is, is there a service worker? There's not. So OK, so we had a service worker before. We built a service worker at CDS, I think, for this website as well. I removed it for this example because it's mostly just going to interfere. And today I mostly want to talk about the buzzword that is code splitting, really. It is actually a bit of a, yeah, code splitting and root-based chunking, actually. Very much intertwined. They belong together. Yeah, because my understanding is that code splitting is, you're going to send one big bundle. And the code splitting is, you don't need to send one big bundle. You can actually break it down into other bits and pieces. And then the root-based chunking is, what are those pieces? Yeah, so the thing is, when you write your code with modules and you don't bundle them, then you pretty much did the code splitting because you never bundled in the first place. But most websites just do a big bundle. They bundle everything up with like rollup, or webpack. All of them, just like. And have like their index HTML, their styles, and app.js, or whatever it has, all the logic, all the JavaScripts. But the point is that if you open a website, you don't need all the JavaScript. You just need the JavaScript for that specific view. And that's how you should split your code. I see where you're going. OK, right, so let's take a look at the code, can we? Yeah, sure. Because I want to see what you've actually done, what you've changed. To be clear, I've not seen any of this code yet. So I'm with you. I'm learning as we go. So if you have seen the episode where we wrote the rudder, you will remember that we have a header partial, where we have the header, the start of the body with a nav. We have a footer partial, which is fairly non-exciting. And then in between the header and the footer, we stuff the content wherever you're navigating. So for example, for the landing page, there's going to be our SCView custom element, which is the current view and the JavaScript that is needed to boot up that view. I see you've got type equals module. Yes. So this is what you were saying. You've now switched this out for the ES2015 module. So all the code I'm writing today is going to run in Canary, because I'm using the modules. You will need Canary. And a flag, right? And the experimental web platform flags. In Safari, you can use it with instant stable, isn't it? It is. Modules are instable in Safari. And then Firefox Chrome. So our custom elements, so this might actually just work without anything in Safari. Who knows? I haven't tried that. No, we should find out. And this is a good point to mention. This is not going to be production code. This is more important than ever before today. Yes. So actually, one of the questions before we start the live stream is, are you going to be writing this code splitting stuff from scratch? Will you be using webpack? And I think the answer is, we wanted to get into the nuts and bolts of what the code splitting is doing to demystify what's actually going on. Is code splitting how do you do it? So yes, I am going to write it from scratch. But what's going to fall at the end is not something that is completely generic and that you can just throw at your code base. No, and there are plenty of tools already out there that do code splitting. What we wanted to get at is, if for some reason you weren't using one of those tools, you might want to. But you might also just want to know how this stuff happens and how the rough idea is, the concepts behind it. I'd like to think we are educationally sometimes. We should get into some coding right before. Let's start. So as I said, I have these views. All these views load different modules. So the about loads the about.js, the contact subsection loads the contact.js. And these files, in turn, import whatever they need. So the contact has access to the database. To be honest, the database file is empty. It's just to have some sort of dependency tree. OK, so you've got these. So you've got a one-to-one map. So contact has got contact.js. So one of the questions that somebody's already asking is that type equals module versus type equals text JavaScript. We kind of jumped straight over and said, oh, we're using ES 2015 modules. I think it's application.js. Yeah, but the might be both. In any case, whichever way you go, the person here is asking, what does that type equals module mean? So that is actually super interesting. So when you use modules, you have to set type equals modules because otherwise an import statement is going to be a syntactic error. So all these imports and export keywords are only available in a module context. And this is how you start a module context. It has another implication named that this script tag is going to be deferred by default. So deferred by default means that if you've got multiple of these same modules in order, say maybe you've got five of them. Yeah, if we had like four of them. Yeah, OK, four. Then they're going to execute an order. But at the end of the loading of the page, right? Yeah, so basically they acknowledge that the default behavior of script tags running right away and blocking everything else was a mistake. So the new type module means do it at the end. And browsers who don't have support for modules will just ignore the script tag entirely because of the type equals module. So there is your hook into progressive enhancement. And that's why the spec also has no modules. So if I want to have a version of this file. If you've got all these individual imports and export statements, then you might choose to make a full bundle of everything where you might actually do something like the code splitting and you put no module on it. So browsers with module support are going to ignore this. And browsers without module support are going to ignore this. And so you have control over which browser loads exactly what kind of code. OK, so hopefully that answers that question. Let's have a look then. So you're saying each one of those contact.js has a bunch of import statements which then potentially has other import statements. So for loads in the miscellaneous, I have misc.js. The misc.js depends on database and video encoder. And video encoder depends on MP4.js. And you can like command click on. No. Oh, is that because you've got a strange. I think it was absolute pass. And they're not actually. No, go back to the HTML for misc. Oh, the HTML. Right, just command click on that. Oh, it's going somewhere wrong. Wow, OK. I didn't know that, though. I'll figure out what it is. That's exciting. There you go. Either way, so basically, we have multiple ways where the user could come to this website on his first visit. They could land on home. They could last on misc. And have to load a certain number of modules because this page, the misc, depends on the, what was it, on SCView, SCViewer app, database, video encoder, and MP4.js. It's like this entire tree of dependencies. Yeah, and that's specific to that section. But then some of those are going to be shared. Every view is going to depend on these because every view is a view. So it's going to need the view, the router, and the app.js, which is our general app. And then you've got other specific stuff to that section that comes after that. OK, so now for about the buzzwords, we already have done code splitting in a way because we have been doing modules and haven't bundled them. So we are only loading the code that we need when we go to a certain website. Yes. But what we can do is we can now do the root-based chunking, where we package together things that are needed everywhere in one common.js file to get more. Yeah, which isn't common.js, like the modules. No, but it's like the common.modules.js. Oh, head hurts. Right. OK. Wording. OK, like shared.js, or this is used everywhere.js. Yeah, that's probably the best name. Obviously, naming is hard. OK. Let's start with it. All right, let's get into this. So I'm going to write an npm script that does this. An npm script. Like a note script. Why? Because I don't want to use webpack because webpack already does everything. OK, so you want, OK. Webpack is an npm script. Basically, we're just doing our own. OK, so what you say? OK, but you're not doing like a Babel plugin or any of that? No, we are going to use Babel. But the thing that does the chunking is a script that we invoke. OK, all right. All right, so I have an empty file which is called build.js here. Do I still have the thing? I don't know. No, OK. You can hit enter. Does it know? Tap, tap, tap, tap, press tap. No, press tap. I think this is something else, though. Yeah, you see, that just types I'm done. I mean, how have you set this up? My friend, it's all very strange. Let's move on. OK, so my entire build is going to be one function. And it's going to be an async function because we are going to have IO operations and therefore, we're going to have to await output and input. And so you just invoke this function. And this is a pretty common pattern I keep using in my daily life. But one thing that you have to think about is that if an async function returns a rejected promise or throws an error, you need to catch that. So if I throw an error in here like this and run the script, oh, I'm the wrong. That's good. There, by the way, googlegithub.com slash Google Chrome slash UI element samples is the GitHub repo where we are going to have this code later on and all the codes from our previous episodes as well. Yes, absolutely. So you can find them all there and have a look around them. Wow, what did you do there to that? It was too close. Wow. Code splitting is the episode. And now if I do build.js, you're going to see this. Let me zoom this in. Unhandled promise rejection because I'm throwing in my main functions. This is something whenever you use async function that you need to think about. And what I usually do is. Yeah, so async function basically says, this is returning a promise. So if you throw inside that promise, it's going to say it's a rejected promise. And we didn't handle the rejection, therefore, error message. And also, we don't see a stack trace or anything, which is not really helpful. So I'm just going to dot catch it, which is the error handler for a promise. And going to do console dot error. And now something tricky, which is error dot stack, which will print the actual stack trace of where my error was thrown. So now we have error handling done, which is cool. So somebody's already saying, is there any point to code splitting? Is it a necessary feature of the web? Yes, I think is the short answer. Otherwise, we wouldn't be here doing this. The honest answer is that even if you, best way to put it, you don't want to be sending down loads and loads and loads of JavaScript. The chances are when somebody hits the thing that you've built, there's only one or two actions that they can probably do. And most apps is like opening a file or just one sort of interacting with a site nav. One thing that they probably want to do. And you want to prioritize that code. So you might end up with like a mega or half a mega JavaScript, which is still a lot. And don't get me wrong, I'd much prefer it if there was less code. But the idea is that this is about prioritization and splitting your code into discrete chunks that you want to ship down. It means that the browser has to do less downloading initially. It has to do with less parsing, less execution, any compiling type work that it does under the hood as well. So all these things. Also bundling in one big bundle is just super cache inefficient, because any little change to any of the original files will invalidate the entire cached bundle. And the user has to redown these 1.5 megabytes of JavaScript, which is not good. So code splitting is very necessary and mostly achievable by not bundling in the first place. And then you can do the smarter bundling by doing root-based bundling. Because the corresponding other bit is that you don't bundle anything and you have hundreds and hundreds of modules. And that might seem like a good idea, but you've just got to be cautious about whether you're making the best use of the network there. And this is where the root-based chunking is smarter, because we could just do a run bundle per view. But then that means a bundle for the about section and the bundle for home would both contain our custom elements, which is not necessary. So that's why we extract those in the third module, which is the shared everybody uses, these dependencies modules. So these have can get downloaded once and reused, instead of redownloading them every time. So hopefully that answers the question. Right, let's get back to the code. All right, so I'm going to write down. So much chat from us today. I know. Code. Let's write down how this is roughly going to look like. The first thing that we need. Top down. I like it. I know, right? The first thing that we need is we need to know what are the endpoints or the entry points to our website. So this is because these are the roots, right? Yeah, so we just need to somehow get those. Whatever that is. I'm going to implement these functions just so we have a way to think about this. Once we have these entry points, we are going to figure out entrypoints.map, because it's a list of entry points. And for each entry point, build the dependent. Let's go full screen. Dependency tree for this entry point, right? So now we have the dependencies tree. So these are the modules that need to exist for that particular entry point. Exactly. It's just like literally the tree. For home, these are the dependencies. For about, these are the dependencies. Then we can find out the common dependencies, find common depths from the depth trees. I really wish it was as simple as writing out those functions. I know, right? It's like, boom. This is what you do. Da, da, da, da, da. So now we have the common dependencies, and then we can build the shared JS, which will be, or let's just call it bundle. We're just going to bundle the common dependencies, right, and turn it into a bundle. And then we are going to entry points for each entry point, rewrite this entry point with the common dependencies. Because basically, we are removing these common dependencies from their imports and replacing it with one common shared module bundle. With all of those in. OK, yeah, I think I see where you're going. I think this is pretty much what root-based chunking is. And let's start with, OK, so you're going to have to do one by one, right, extract the entry point. So I'm going to do this. And then I'm going to console, log, entry points, because I want to know if my function does the right thing. So the first thing that we need to do is we need to figure out how to get the entry points. And I thought I would go back to the 90s for this. Because what this website has is a sitemap. So if you remember sitemaps, those are used by search engines to figure out which pages to crawl. If you have in the root of your domain, you can have sitemap.xml. I think I actually have one. And there you just list what the pages are. And I have the root about contact and miscellaneous. And we're just going to use this because, you know, why not? Sure, why not? So this is going to be an async function because we're doing file operations. If you've not done async await and it's brand new to you, then have a search around. You'll find at least one article, I think, by Jake, who goes into some details on what it is, how it works, and so on. It makes stuff so much easier. Yeah, it allows you basically to use promise-based async, not surprisingly, but promise-based code, as though it's synchronous code, is the long and the short of it. Stuff like this file operations, networking, I.O. It's just easier to reason about when you don't have the, it used to be callback hell. Now it was promise-hell. And I think async await is a really good and convenient solution to all of this. All right, so the first thing that we need to do is sitemap string is, we need to read a file. So read file app sitemap.xml. Sure. So now everybody who's done some note will know that you need the FS module. It's the file system module. But file system operations use the old callback syntax and not promises. And we are using node eight, and that means we can change that because what they added is promiseify from the utl module. So. What's the German for promiseify? I love it. I, we should act. Fersprächifizierung. Brilliant. Yes. Brilliant. All right, and what can, people might not get it. So it's. No, no, no, no, leave it. Leave it, leave it, leave it, leave it, leave it, yeah. So, I mean, we can always change it before it goes to the GitHub, but for now I actually want it there. Probably if I, oh, okay. So what this does, it takes any function that conforms to the standard node function layout, but the last parameter is a callback that takes error or the result. Yes. And turns it into the same function called that returns a promise. Okay. So what we can do is we can call, give it fs file read, read file, read file. And this constant will now behave exactly the same, but return a promise. So what we can do, we can just call away here, and that should work. And then we can go sitemap string is sitemap string to string because this will going to be a buffer, ujf8, and just for testing, I'm going to return sitemap string and see if it works. While we're here, in that code, you have the curly, oh. Oh, of course it's not kind of. Oh yeah, just call it promiseify. Oh, okay. It's my fault, isn't it? Promiseify, let's call, this word promiseify is, you know, I can pronounce the German but I can't type the English word. There you go. There we go. Okay, okay. We're reading the sitemap. That's cool, right? Okay. So you got that back as a string, though, right? Now it's a string. And now, obviously, this is XML, so we'll have to parse XML. So I'm going to use the good old XML2JS package. Interesting. So presumably we now need to install that with, or have you? I've already installed it. Or you've MPM installed? Because I don't want to rely on MPM install actually being quick. So. Okay. And the best part is, even though it's an auto-modulate, because it conforms to the structure, we can actually use it on here as well. So there was a question about the curly braces around promiseify. Did I say that out loud? No. Okay, yes, because we reached the book just as you, as I was about to ask. So the curly braces there around promiseify, do you want to explain it? Yes. That is called destructuring. Basically the requireUtil returns an object that has multiple, so what we could do, this is the same here. Here we store the entire object. Which is the module.export object, right? So it's the same as if we call this utl and then went utl.promissify and did something with it. But with destructuring, you can basically selectively import, which is more similar to the ES6 import segment. Yeah, you're saying I only want this one thing from that entire object. So please just get that object and then pull that one property out and give me that back as this, in this case, promiseify. Yeah. So let's do this. Sitemap await powers XML sitemap string. And let's return that and that should hopefully be some kind of structure. Yeah, so it says object, object, object, but... I mean, since there are four of those and then there's four URLs, I'm going to take it on faith that you actually got this down. So now the only thing we need to do to extract the entry points is we are going to go to sitemap.urlset. So that's that, okay. And then we're going to map those and go to... So that's each one of those objects. Yeah, because we have four URL children. So this is an array of URLs and each of those has a location.lock. And so if we return it... Dude, that looks gross, dude. This should... Ta-da! I mean, that looks nicer, though. And now the last thing I want to do is like strip off the domain name. And so you have a URL. Now I actually need URL. Okay, so you're going to parse that URL that you got back and do something... Yeah, new URL, URL, path name. Okay, so that just gives you the slash about, slash contact, slash something or other. Ta-da! And these are our entry points to the entire page. Ah! Ah! Ah! Ah! And that is basically our first step down. I want to do a little in-between step where I turn these into the sections because as I've said in the code, if I say about, I have the about.html and the about.js, so I just want to work on the name about, not on the about.html because we're going to work on the JavaScript and not on the HTML file. Not sure if I'll leave it, keep typing code because I find that easier. So, entry points. Dot map is an entry point and I want to now need another module which is going to be the path. So we can do simple I want path.basename of entry point and I want a path.xname entry point and this sections. Lowcase s. Yes, you're correct. Look, almost. I mean, this looks pretty good. The only thing that we need to do, tick, tick and then map entry point. If entry point is empty, then we use index, otherwise we use entry point. Got it. So you're just making sure that empty one at the start. So these are four sections that we need to process. So we're making the assumption which again in a generalized production setting you wouldn't make this assumption is that each one of these sections in the sitemap.xml maps to a.js equivalent. So, like about slash about has an about.js. It's true for our test site, not for anyone else. If you're just joining us, firstly, welcome, hello. We are in the process of talking about code splitting and root-based chunking. What's root-based chunking in German? Rutenbeides klumpen, vielleicht. Okay. We were just apologizing. Yeah. Okay. Okay. The idea of being that rather than just saying, you know, go off and use one of these tools and save webpack will be, I think probably the one that most people go to, which is great for production. Absolutely. You want to use the best possible tools for production. We wanted to kind of demystify and explain some of the principles behind the tooling itself. So what we've decided to do is just talk about if we started with a sitemap.xml and our previous sample with server-side rendering, starting with a site with a bunch of JavaScript that it's importing, some of which is shared between the various sections of the site and some of which is unique to each section. How do we split the code? What would we actually do if we were going to do code splitting on our own without one of these tools? So that's where we're at. And root-based chunking. Root-based chunking. And we're going from there. So we're in the middle of basically reading through the sitemap.xml. And that's what we've done with that part. We've done with that part. So we now have our entry points. Yeah, we have basically our entry points of the JavaScript files that we know we're going to go up. There they are. MISC, index about contact and MISC, which is, you've pretty uncommitted. There we go. Those are the sections. Great. So let's work with sections instead of entry points. So we're going to go with section, section, section. And this is the first time we're going to encounter Babel. So the first thing that we need is we need our code. So we're going to read file, app, static, section.js. Okay. So now actually that's not quite true because I always run into this issue with a way that you would have to put parentheses and two string in one line. And I don't think it's very readable. So I'd like to break it down in two lines where you do the string conversion afterwards. So now we have, sure, the code in a string. And now it's going to get interesting because we're going to write a Babel plug-in. See? Yes. See how fun this is? Const Babel require Babel core. The core is what we need. And our plug-in is, plug-ins are most of the time. I mean, there's multiple variants of plug-ins in Babel and the only one I'm familiar with is AST transforms where you can transform the abstract syntax tree that Babel gives you. And the way it is implemented is that you can implement a visitor pattern. Some of you may have had it and you need some of you may have not. This is Vista. I think you want an eye in there. Was he tall? There we are. And this visitor object can contain functions or methods rather for any kind of token type. So the one we care about is an import declaration. So I feel like it's worth pointing out. An AST is an abstract syntax tree which is once your code has been parsed and tokenized and parsed and everything else, you get this tree which indicates all the different. So you have a program at the top. Usually you have an array of instructions instruction is like parameters and all those kinds of things. Function name, parentheses, parameters. And so what Babel would typically do would be it would read through your ES2015-ish type code. I like 100. Because it varies, right? But it reads your code and it turns it into this AST and then through a bunch of plug-ins it will transform that code into what we spit out on the other side. And we're now one of these plug-ins. We're gonna make one of these plug-ins that's gonna sit between the code that we started with which, as you said, does actually run in Canary with the flag switched on. But we wanna do something that would transform it a little bit more. So what we do is we create one of these, you say this plug-in which has this visitor pattern which basically means you kind of register these callbacks. And you say please call me whenever you find an import declaration. You could also do functions like a string, literal, which you get called whenever there's something in double quotes or single quotes, but we don't want that, we only care about import declarations. So if you look at this documentation on how to create these plug-ins, right? And they would tell you- I recommend the handbook that we are gonna have a link in the description where you learn how to write plug-ins. So there you go. So we're in the middle of just basically going, we know that we want, whenever we see an import declaration, we wanna do something with that. Which is, in this case, we want to build a dependency tree. So we get the declaration as a parameter. And what we really care about is the imported file which, and I have to admit, I just memorized it, is declaration.node.source.value. Sure. Actually we could do, we could just print this out to see that it works, imported file. And then we're gonna babel.transform on our code and our options take a list of plug-ins and the only plug-in is our plug-in. Cool. This can go. Actually, we can also keep it, it doesn't really matter. We're gonna return something here later. So let's see if this works. Yeah, you see basically, it now went through all the files. It's a little bit unstructured in the output, but we can see that all the imports that are imported at some point, one way or the other, we get called. So this is a really great case of building on, standing on the shoulders of giants is really what it is, right? It's kind of going, I don't wanna build an abstract syntax tree myself. I don't wanna do- Or you wanna fiddle with the regular expressions trying to parse Java. This is try and test that code. This is great code. We can make use of it. As you say, we just hit that import statement and we can just kind of go, what import are we actually seeing here? So this is really cool. Okay. And what we wanna do, really, is we want to collect the dependencies. So I'm gonna do an array. Mm, stateful. Yeah, a little bit. I'm not sorry. No, not even a little bit. And I'm gonna push that dependency on our imported, I'm gonna push the imported file onto our dependency list. And a dependency list is what I'm gonna return. And now that means that our depth trees should now be an array. Which should look pretty similar to what you just did. It is a promise. That is true. So I am gonna am await promise.all. Okay. And now, boom. So now we have an array of arrays. This is the home root where we only depend on three. This is the miscellaneous root where we depend on four. But if you take a closer look, you will see that the mp4 counting. But the mp4.js is missing because we are not actually looking at the dependencies of our dependencies. You're not going down the entire tree. So this is just the first level? This is the first level of dependencies. You're okay. So can we fix that? Is that possible? Yes, we can. Yay. So back to our, basically what we just need to do is we just need. Just need? To call. This. So we kind of recursively call it down. We see an import. Now it's a little bit tricky. So because we now have to do another IO operation, which is async, but Babel is inherently synchronous. Oh. So something you can do is just do an anonymous asynchronous function that you invoke immediately to kind of do it async. And then in here, we do, no, that's not true. Const code is. I'm glad you know what you're doing. Cause right now I'm watching this going. I mean, it's a complete mystery to me. Imported file. So what are you doing? In this async find, you're going to read the imported file and then call our plugin recursively. So reading that. Sure. And we're doing that. So I get that you're okay. Uh-huh. Interesting. Uh, do you not have to wait anything on that function that you're calling immediately? No. Okay. All right. I think this is fine. Should we find out? Yes. Let's find out. But we need to, but now we have one problem which we can fix. Ooh, ooh, this is good. I can plug my side project. So surma.github.io slash under dash. The thing is that we are now have, we're putting more arrays into our arrays. So we want to flatten it. Yes. Okay. Cause we don't care. All we care about is getting a kind of a top-level list of hero all the incos. All dependencies. And there is no default flatten on an array in Node or JavaScript. And so you can go to my project which is called under dash where all these little like useful functions are there for you just to copy. It says right there. Just copy, paste what you need. Yeah. So if we need flatten, we're just going to go with this. Hey. Just going to copy that. Look at that. We're going to paste it down here. Of course you would. Why wouldn't you? And then. And. You're going to flatten the depths. You're going to flatten await promise.all. I don't even know why you're doing that. But okay. Cause there's still promises in there. So definitely when you said it was easier to read the async await code. See? I was wondering whether that. Oh. Yeah. This is sneaky. I think. Is it? Yeah. Okay. Listen. As I said, Babel is synchronous. So this async function is going to get actually executed after all the synchronous code is done because it's a micro task. Okay. So we need to push something onto dependencies right away. So I'm not going to. That's what I was going after. I was thinking. I was thinking you need to do something that says there's something coming in here that will need to be processed later or. Actually. We are going to return await. This is so much cleaner. Dependence. Dependency. Tree. Now this makes much more sense doesn't it? No. No? No. What are you doing? Do you not need to pass that something? Yeah. What we're going to do is we're going to pass the imported file. But then we need to need to turn this into a file. So. Oh, so you need to call that with the initial. So we're going to put this here instead. This is a file. We're going to read that file. Now we have a function that returns all dependencies of a file and we can just. And then recursively goes down and finds the next one. OK. Should we see if that works? I'm a little bit wary, but that is correct because it is a slash static path. I need to anchor it to my app folder where all these paths are. All right. Yeah. Hooray. Now do you still. You have MP4 now. Because now we're actually cursing down the. Yes, we have the dependencies of the dependencies which in theory could have the dependencies of the dependencies. It will go down all the way. All the way down. OK. All right. Now this is actually step two done. We have built a function that builds a dependency tree. Yes. And recursive down. So now we can go on to the next step and talk about figuring out what the common dependencies are. Oh no, I feel like this is, you know, what should be. How would you do it? I mean, interesting. Right? So find common dependencies from our depth trees. How would I? If you literally think about the trees, all you really want to do is just find nodes that appear in all of these trees. Yes. So what we're going to do is. Are you going to do like an n squaredy type thing where you take a node and then look in all the other nodes? I don't even care what the runtime is because this is not product ready code. As soon as you don't find it, the worst case would be n squared. I think so. So I'm going to flatten the tree into a set. And then for each item in the set, I'm going to check if it's here in all the trees. And if so, keep it. I think code will be better to visualize. I know where he's going, which is just as well. So we're going to do a new set. Yes. You JavaScript sets now because then we don't need to worry about filtering out duplicates. Set does this for us. Yes. If it's in the set, like a. So depth trees for each, which is one depth tree. So it's like the one that's for home, one that's for about, one that's for contact, one that's for misc. And each of the depth tree has, for each, has a dependency and depth set at depth. That's it. So the folks are on the chat. Hello again, folks on the chat. I'm really enjoying watching all the chat to go by. And there's a lot of people picking up the fact that we're going for sets. I think some people, first time they're seeing them. So that's really exciting. They're really fast. They're very good, really useful primitive to play around with. So for this kind of situation, they are actually quite useful as well. It's just easy to grasp. You're like, oh, it's a set. There will be no duplicates because that's how a set is defined. You can't have the same thing twice in a set. And we just want to have a list of all the dependencies that appear that's a set. Can you not make a set from an array? Can you not like? I might. Let me just check first if this works. OK. This looks like all the dependencies. I mean, for this number of dependencies, it's not a big deal. And probably for most projects, unless you've got thousands of dependencies, this would be micro optimization. So carry on, carrying on. It was just me being curious. So now we have a set. And what we want to do is we want to turn that into an array and then filter on it. Because you want to filter out the things that do not appear in every tree. OK. So for each dependency, we want to check if for every depth tree, depth tree does not include this dependency. Let's do this explicitly, because otherwise it's going to get too noisy. No, you want every tree to include this dependency. Do you not? You want to filter out the ones that, ah, now I'm actually not quite sure. Let's try both. Yes, you're right. We want to keep the ones that are in every dependency tree. Yes, that's why we call them the common dependencies. Yeah. I think I have to go. Thank you for coming. So as you can see, now we have a set of dependencies that appear on every entry point. And that is correct, which is amazing. And this is actually five lines, which sometimes amazes me that you can express things that are harder to explain with words in, like, snappy, snap, snap code. And do you know what? Some of these newer, they're not new new, but they are newer, definitely, operations for one of the better term, I suppose. Methods on the arrays and so on are hugely significant, I think, filter, find, find index, map, reduce. These are all in- Reduce, I still, I use reduce a lot, but I still feel tricky using it. Like, I feel like I'm not actually easily conveying what I'm trying to do. I liked it for, like, promise change, change, we could change together promises. You reduce the change as a single. I feel smart doing it, and that's usually not a good thing. No, it shouldn't be smart. In a few months you look back going, what was former me thinking? Exactly. Yeah. Okay, we now have common dependencies figured out. So now let's bundle these common dependencies into one module. Okay. Right? So what are we gonna do? We are gonna write that function. That is what we're gonna do. We're gonna write the bundle function. Function bundle, and bundle is gonna take a piece of code. And this is interesting because that is- What are you doing? I'm just gonna quickly generate some quick and dirty code because that's just, I think it's easier to do. Okay. So- Hence, if you're just joining us, we're making our own code splitter because why not? Because we wanted to just talk about the kind of things that go into code splitting. And it's not production code. We always wanna be very clear on that and what we do in Supercharge. This is very much about us sort of sharing a journey and a process and an idea or concepts. And sometimes the code is a good starting point for something that you might end up doing in production but certainly something like this, we are- If you wanna put in a lot of work, you can productionize this. Yeah. But there's still work to be done. And there are existing tools that already do this. So- Yeah. Well-known tools, well-explored tools, tested tools. Yeah. Rather than- Reinventing the wheel is usually, should be avoided. Unless you want to learn about wheels, which is exactly what we're here for. So, let's carry on. So, I'm just gonna console log the old code, the code that we are generating just to see it. I'm actually doing the right thing and I'm comment you out. So. Old code. Old code. There you go. That's just the old code. It's the old code. Gets me every year. Okay. So this is- Yeah. That is what I actually wanted to do is I wanted to join this with a new line. Okay. So we should now have like pretty much an empty file that just has three imports. So this is now the file that we wanna roll up, basically. Okay. So we are gonna write our own Limpul roll up real quickly. Oh, sure. Well, we're just gonna import all these files that we need. Okay. Sure. And we're gonna do that with a Babel plugin. Woo! More Babel plugins. So we're just gonna copy and paste the old stuff. And for each, so we're gonna have like new code as an empty array. And for each imported file, we are going to, let me think for a bit. Let's remove this. So, we're gonna read that. No, we're not gonna read it. We're gonna go recursive again. So, we are gonna bundle, await, read, file, app, imported file. Tell me what you're doing here. Okay, I'm gonna explain it. Okay, I'm good, because I don't understand it. So, this plugin for Babel is gonna take a file and whenever it encounters an import, it's gonna remove this import with declaration.remove, which removes that import, and then inline that imported file. But before doing it, it has to do again the recursive thing. Right, so if the thing that it's gonna replace has imports, it needs to replace those calls itself. Okay, all right. Okay, so what it does. You have a new code array at the top. Do you need to do something with that? So you got declaration.remove, but presumably wanna push. New code. This is where we wanna push. Okay, wanna push it in. And now we're gonna have to do something new. So, because before we just ran Babel, and as I said, Babel does code transform, but we totally ignored the output beforehand. Well, we take it and do something. No, we take the code, we do a transform, and then we ignore the results, which we now actually need, because now we wanna have the code and it's without the import. So you're destructuring again to get it the actual code? I'm getting to the code and now we push that onto the array as well. And then we return the await promise.all new code. Gonna flatten it and we're gonna join it with new lines. Wow. So there's a lot going on in there and before you go and run that. So the reason you're doing promise.all around new code is because some of it will be a value, a literal value, but some of it will be a promise, like the call into bundle again. So you've got a set of, or an array of some literal values, some strings essentially, and then some promises. So what we're doing is we're saying, wait till at least all the promises inside that array resolve, and then we'll have an array of literal values at that point, which we can then, so you're waiting on that happening, then you're flattening any, because you've got that recursive kind of thing going on. So you flatten all that down, and then you join those with new lines so at least it's tidy. Yes, okay. So let's try and see what happens. Share JS. If we print this for the console, new. New is missing a, Oh, because I made, No, it says it says it's way. Yeah, because the wait is, go on and let's see what happens. I won't be addressed to your codes not defined. Which line, 39. Now somebody's asking, what if the imports are circular? Oh, this one. Old code. So. Yes, I heard the question, and I'm gonna ignore it. So are you basically implying at this point that we are not considering circular code? We are not considering circular. Which is what I assumed we weren't as well, but again, this is another reason why you'd use, like under the top. Rollup has figured that out. I think Webpack as well. I mean, I have an idea of how you could do it. If you were to use bundle, you would probably have something like already imported dependencies as an array and skip the ones if it reappears. It's a graph theory type moment where you say, I visited this node, don't revisit this node. This is usually, we try to keep it, you know, that people can follow along. And I thought about it, and I purposely did ignore it because it was just out of the scope for that. We're ignoring it, I think. Something that, so as you can see, we now have, whenever there's a copyright error, this was a separate file. So now we have concatenated multiple files into one, which is cool. So now the only thing we need to do is write that file. So I'm gonna await, oh no, ooh, we need another one because now we need to write file. What did I do? I don't know. I pressed the magic shortcut. So write file and then down here, we can write file which is going to be app static. I'm lazy. I'm gonna say all generative files start with an underscore. Okay. Because why not? ShareJS. And let's go back here. Let's run it again. Should be nothing. And then in app, static, shared, boom. Okay. And now for the final step of this journey, we have to rewrite our entry points because right now, yes, we generated sharedJS, but nobody's using it. Yeah, everybody's still back in the original set of imports that they had. So every time we presumably find those imports together, we should be able to replace those with what you've got here in that sharedJS. So async function, it's gonna be async because we have to write files. There's gonna be IO, it's gonna be async. Okay. That's the logic I usually go through in my head. I think we have, for example, the find common depths was not async because we're just working on the tree is already there. No reason to make it a synchronous rewrite. We're gonna rewrite actually not entry points. We are working on sections, section, section. We take a section and we take the common dependencies because what we're gonna do is we're gonna write a third Babel plugin. Wow. You just all open that. Yeah. So that's also a thing. You probably normally wouldn't do that, but I'm not an expert in Babel. I just threw Babel plugins together and do three passes. It's not efficient, but it works. But it conveys the concept. Okay. Did I say it's all production ready code? We may have coded that. All right, so what we're gonna do is we're gonna go through all our sections, each one by each individually. Okay. And basically remove all the imports that are already in the shared one. Yes. And then we're gonna prepend an import for the shared bundle. Yes. That should work and let's figure that out. All right, so I'm gonna do something I've done before. I'm gonna copy my plugin. If nothing else, this is, I think, showing why some of these tools exist, right? Yeah, it's hard. Because writing some of this code is, you know, it's pretty challenging. Yeah. You know, and yeah. The cool thing is this one is actually one of the best, the easiest plugins. So what I can do is if common dependencies includes imported file, then remove it. And that's it. So, okay. So we have the dependencies. Common dependencies is a list of models. Okay, so like the scvue.js. So you're saying if this file contains that one, get rid of it. Yeah. Because we should know that it's a common dependency because we've already figured that bit out earlier. So we can be safe to remove it. Okay. So now we're gonna actually run the plugin again. We're gonna transform our old code. We're gonna need to read the code first. I forgot about that. So const old code is await, read file, app, static, section.js. Somebody's caught the bug for you. Const imported file includes imported file. Thank you. That was a quick turnaround. I don't know, right? Yeah. Thank you very much for that. Thank you. I also appreciate a good bug fix. We are reading the code. I did have to do the thing again. Old code is old code to string. Also not very optimized. I'm reading the same file. I think the third time now, which usually wouldn't do. Yeah. Okay. We are removing all the imports. This will be the code without the import or in the shared module. Okay. So we are going to... Now we need to prepend our... We are gonna, that means, you know, let's let it again. Let's make it a let so we can know. Then we can say code is import. No, let's do string. I'm gonna need the single quotes. Static shared.js is what I call it, right? Yes. Shared.js up here. And I'm gonna prepend the old code. Even gonna give it a new line. I'm nice like that. And then we're gonna await, write file, app, static, section. Okay. So you're now rewriting the about.js to include... With an underscore, because all my generated files start with an underscore. Okay. All right. So code. And this we're gonna have to await promise.all. Why? Because each of the rewrite functions will return a promise until the writing action is done. You don't need to map it then, instead of for each of it. That is correct. Look at you. Hey, you know, occasionally I have my uses. So this should not output anything. But we should have app, static. About.js. About.js, which just imports shared.js, which is correct. Yeah. And then was it Misk that had that plus the other two? Flop. Okay. So yeah. So it's doing its job. It's doing exactly what we... It's a little bit hard to read because so that's the two imports. But you've got that shared.js at the top, which is the ones that are shared, obviously. Yeah. And then the two that are specific to that section. So we've done, we've effectively done, we figured out what's common, put that in its own file. Yeah. Was that shared.js that was flattened, right? Yes. So that's all been flattened. Somebody pointed out as well, that we don't do anything about sort of conflicting variables or whatever across those shared. Exactly. We also don't support the import sub-object from, whenever you have these kinds of imports, this would already break. So... Yeah. It is production ready. No. No, no, no. There's always times you can say it, right? But hopefully you are getting the idea. You're seeing, you know, to be fair, you are seeing like async away. We're seeing promissify. We are, what else have we got in here? We obviously Babel plugins. There's loads of stuff that's in here. Yeah, of course. You know, there's all sorts of interesting techniques and ways of thinking about these problems when you, even if you just try some of this stuff yourself. And I do recommend sometimes doing the top-down approach that I did. You write down a sketch of the calls you want to make and what data you need to move from A to B to get to your goal. And then you go down the rabbit hole. Naturally, I'm a person who starts starting with the individual puzzle pieces and then tries to build a house. And then, yeah. And sometimes I have built a number of functions I'm never going to use. And this way, it's easier to walk towards a goal. That's underdash, isn't it? For example. But nonetheless, so even though we're pointing out, yes, it's not prediction, you're ready. Yes, there's lots of edge cases that it doesn't handle. Hopefully, you are seeing some of the ideas and the concepts that are at least would be behind a professional tool. And you can see why people have spent so much time and energy to put these high-quality tools together in the first place. I mean, there's basically only one thing left for us to do, which is showing that it actually works. Now, at the moment, your HTML refers to the non-generated version. So all I have to do now is to go through this HTML and add an underscore, really. And that should make us flip over to the new generated versions that have root-based chunking. So I'm going to go to Canary. I'm going to go to the router. I'm going to open DevTools. I'm going to look at the network panel. So we should see GED.js. Should we not? Yes, let's see. Yes, we're going to see the index.js and the share.js, which is all the index file really needs. And if we go to Miscellaneous, we should see the Miscellaneous.js, the share.js, database, video, and mp4. Yes. In this case, it only loaded the ones that were not already there. But you can see, we've switched pages and share.js wasn't reloaded. But this is a nice feature of the modules, right? Yes. The modules are automatically kind of handling this idea of, oh, I've already got this import. Why would I get this a second time? Yeah. But we've made the most of that single import now. Because in that single import, the shared one, there's actually a lot of code. So it's sitting somewhere in between. I have an individual import for every single part of my app, which could give you hundreds, if not thousands, of imports versus one. And also, it can get really bad in terms of loading time, because the browser won't know what to fetch next before receiving the first one and saying, oh, there's dependencies. I need to go out the networks and grab those. And non-trips are really expensive. So what we're saying here, I suppose, is that on the one hand, you could have this big bundle, which is what a lot of sites are doing today. And this is where we've come from. On HTTP1 in particular, you would bundle everything into app.js. And that's it. And it serves every single part of your app. And sometimes those are huge. We know that people have a lot of functionality they're trying to pack in today to their stuff. So that's the old way, if you like. You could go completely the other direction and be like, I've got thousands of imports. But that can be, as you say, so optimal. Which I knew during development on local hosts, that's fine. But in production, that is going to tang your loading. And what this is doing is it's bringing us back into that sort of mid-ground. It is a spectrum. It's not always black and white. Exactly. And it's going to really depend on your context as to how far you can push this. And obviously, while we're doing this today, specifically for our particular app, you want to investigate ways to do some of this stuff in your own code for your own apps. Something we haven't done that is something you could add on top is another buzzword is tree shaking, where you remove the bits of code that never get used from your modules. Because sometimes. That was the buzzword dance that I was just doing there. Oh, yeah. We have a buzzword dance now, apparently. Sometimes some projects, even though they are already in modules, still have here's all our formatting functions in one module, but you only use one of them. And tree shaking is the process of removing all the code that never gets called into it. And again, if you think this was a bit challenging. Look into that. Tree shaking, I think, is out there, isn't it? Yeah. Cool. That's pretty much everything I wanted to talk about today. Awesome. So with that said, thank you very much to everybody who's been joining us on the live stream and in the chat. Thank you so much. We've had a blast. It's really nice to be back. And I guess, until next time, I've been Paul. And I'm Soma. And this was supercharged. See you later.