Video details

Mike Hartington: Deep Dive Into CLI Builders


Mike Hartington

Watch all the ng-conf: Hardwired presentations/videos at
ng-conf: Hardwired is brought to you by:
- - -
ng-conf is a three-day Angular conference focused on delivering the highest quality training in the Angular JavaScript framework. 1500+ developers from across the globe converge on Salt Lake City, UT every year to attend talks and workshops by the Angular team and community experts.
Follow us on twitter Official Website:


Yes. So we are going to talk about the CLI builders. So far, what I've come find is that there's like three or four people that know builders really pretty much in detail. And they happen to be on the angular team. So I hope that at the end of all this, you at least understand what builders are. And maybe you can start to think about places where a builder could make sense in your project or in your company as a way to integrate with different systems and different build scripts that aren't necessarily part of an angular process. So rusty subdomains Mike Hardington work for a company called Ionic and Angular GDP. If you have any questions for me at any time during this, tweet me love to know what people are thinking and Hardington on Twitter. And if you want to copy this link real quick. The slides are live right now, so you should be able to get everything that we're gonna go over as well. Some video assets. So let's dive into it first. I kind of understand what a builder is, because if you survey a group of angular developers and ask them, hey, do you know what builders are? Chances are they're going to say, aren't they? Those things that, you know, I call it never. I run a engy command and this is kind of true. They're similar to that, but they are so subtly different. So if we were to isolate the characteristics of a builder, to describe them, they're really just kind of mysterious behind the scenes aspects of the CLIA. You don't interact with them directly and you shouldn't really have to interact with them at all. UCLA itself is what's going to be the one that it's calling a builder and handling all of that. And they operate under very simple principle that we'll see as we go forward. Now, we're already familiar with a handful of builders. When we start a new project. So whenever we go ahead and we run energy built, well, what we're really running is the browser builder from Angular Delicate. When we go ahead and we run engy serve. As the name hints at, we are running a def server builder from Def Get. So we are just running this builder by running our I men. And the last one is whenever we run engy test, we are running by default. The tests test run. Or we could swap that out for something like just so the high level energy commands don't actually know any of the implementation details. They just know run this thing which will run a builder. Now, with all of these different kind of builders that we are interacting with, they operate on this very simple principle, given a set of options. Run this function and then just return to results. There's nothing about a building that says build my app. The names kind of a, you know, an unfortunate naming incident. But builders just knows. Here are the options. Here's a context of which I am running in. I'm going to do something with these options and then return something. Whatever that is, I don't know. So let's break this down real quick so we can see how all this comes together. So if we have a vanilla, angular project, we are just opening up our angular Jason file. We're going to come up and look at this build object. This is what we consider our architect, not an architect. We think about the real life equivalent. They aren't very. They're not into the details of how something gets built. They just describe the higher level ideas and design of what is going to get built. So when we run, build energy, build or energy, run at Colen build. This is the architect that we are calling, so we will call our architect and then our architect is going to go through and say, hey, when I get called execute this builder, this builder is just going to go up and we're pointing to the angular debka package and it's going to run deep build, angular browser filter. So pretty simple. Look at there. We are going to get our options from the command lines and once that we have hardcoded in the file and then we're also going to provide some overrides or maybe we're in S.I or we're doing a production builds, we can start to modify certain options. So this is how a builder operates than pretty much every single use case. Let's go ahead and just make a building to see how how everything works. But I think it's first important to answer the question why? So if you want to learn to build a builder having a good use case and why you would want to add one is going to really help you figure out what you need to do next. If you have different tools like image optimization tasks that are based on NPM scripts, a builder can be a great way to integrate those tools into your angular CLIA project and have them feel like a much more natural part of your project. Plus, it's pretty cool to be able to know how to do something and then go to your team, say, hey, look what I figured out how to do. You don't know how to do that. I do. Job security is what we're all looking for. So our process so far for creating a builder is just to make a directory and do npm it or yarn in it and create a package chaser. Now, instead of our package, Jason, we're going to obviously give it a name. Version number, and then we're going to add this. He called builders. He's going to reference a another Jason file. And we are going to call this one builder such a son. Now, instead of our builders that Jason file, we are going to create a list of different builders. So here we have builders as a key. And inside of that, it has a list of other builders that we can work with. So, for instance, this is our command builder. Here is the implementation for it. So this is what gets executed when we call the builder. This is a list of different options that we can pass to to our builder. And then the seal. I can use the schema to validate saying, hey, this is not a ballot option that you passed. Fix that. And then to close it all out. We're just going to give it a description because we're good developers. We want to make sure our tools are basically as self documenting. Now, if we kind of piece this all together, we have the package, Jason, at the top. The builder dot some at the in the middle. And then the key for our builder inside of our angular dot, J-Star. So if we can see the look up, we have the package name inside of the package, Jason. And then the angular dot, Jason. And then we have the builder name kind of doing this nice little Look-Up. So when we call at Bionic Builders, we are going to look at the package version. It's going to read our builder, not Jason. And then we're going to call the builder that we're passing in after that Colan. So let's take a look at the implementation detail and kind of understand how this is all working. So we'll create a file called command builder. And Command Builder is just going to have this default export of Crete builder. Now, the CLIA operates on the standard principle of if you see a function that is exported called create builder, run it and then it will handle wiring up this whole entire process. So our defo export crate builder will just get called by to see Allai. Going to pass in as an argument, a function that we are going to write ourselves. Now this is the actual logic or builder. Holtmann builder. No candidate gets two arguments here. We have an argument of options which are just options coming in from the from our schema or from the angular dayson and then the contexts, which is the current running contexts that the builder is operating in. This is all kind of inherited from the satellite. So you should be able to get information like your workspace through different builders that are also available to this particular project and whatnot. Now, this is going to return a promise, but important note that you don't actually need to return to promise. You could return unobservable. Or you can return. You know, a synchronous method doesn't really matter. But what matters is that we're returning a value of built event. So let's go forward and expand on this. We are actually going to use nose child process and create this new process, grabbing a command and then some arguments from our options. So these are going to be things that users can set. And we're just going to spawn a new process from here on out. It's fairly typical node programming. We have process that standard out every time we get data. We're going to use context that logger dot info, which is our standard, which is a standard node logging service to log out all that data and send it to string. But the important thing here at the end is that we are going to resolve our promise when the process has closed. And then we're just going to resolve with an object that has a key success. And it's either going to be true or false based on the exit code. The exit code is zero. True means everything has been successful. So without imports, obviously, we're at like 20, 20 ish lines of code if we exclude the empty line on line number two. Not too bad altogether. So let's go ahead and look at this in a life example. So I have my steel eye over here and I just have this angular dot Jason package file and I have the command builder setup right here. See, here's the key that we will run. The architect here is our builder. Where we're calling bionic builders and then setting the custom built or Look-Up that we've learned and then here are the different commands that we're going to run. So if we're going to just run at less than passing the flags, dash L and A, whenever you do a normal built but on a production build, we're going to use the tool house today and pass in the argument. Builders are cool. So let's just run this real quick. So we'll say engy run demo app command build command builds. So our builder has run. We're just listing out all the files inside of this directory. Now, if we are to run this again. And then passing the production flag, we're gonna get a different output based on the tool Kotsay. So this is how it works here is we have different options for production and for non production use cases. And video just in case. But that's fairly simple. There's nothing in there that touches I mean, the build processes and really there's no there's no actual real life use case for that. What about something a little bit more complex that involves Web pack, because as soon as you add Web pack to mix things get a little bit more complex? Well, let's dive into this and ask what we can get. So starting off on these same kind of base that we had before we had this creek builder function and we're going to pass in a new argument called Extend Builder. Now, in this case, we it's looks similar to our command builder. We have option in context getting passed in and then we're going to return an observable a builder output. Now, from here, we're going to start to call different function at functions available from Dev, Kit and the source one is going to be target from target string. Now, at Target from Target, String is going to go ahead and take a builder name as an as an option or as an argument and then return the different details about that builder. So it's going to give us the project, the target or the actual architect that we want to run. And then the configuration or so if it's production, S.I, what have you. Let's we have this target information with we're going to call target specked. We're going to use it to get some more information from our project. So we'll use Fork join and we're going to call context that get target options and context up. Get builder name for Target. So the first one is going to go through and get all the listed options in the angular dot, Jason. These could be things that whether or not we want to include certain files or certain assets to our builder. We're just trying to get all those in return them to. Ah ah ah ah. Current builder erm here context to get builder name for Target is actually just going to go ahead and get us the the Look-Up or the builder. Key information here. So once we have the project, the architect and then the configuration, we're just going to go ahead and get the build a key. So this would be something like angular dev, kit, slash build, angular Colen browser. So it's that same kind of package look as before. And once we have both of these pieces of information, we're going to go ahead and put that into another call. Another function called Tuite from context called Validate Options. Now Velda Options takes the options that we have in the workspace, file a Valdese stand against the schema. Now, a really great example of this is in a previous builder that I was working on. I didn't call VLT options and I was trying to figure out why Libris Low Server wasn't library loading. Like any changes I made, it wouldn't reload. It turns out there's an option on the dev server builder to disable library load because sometimes you wouldn't want a def server to library reload, apparently. By just adding a call to Velarde options, I could validate the options from the workspace file against internal options from the builder itself and get the complete picture of what are the default. So I should be sending to this task. And what are the ones from my that my user has set inside of their workspace file? So that's all well and good. We'll call that. It's a promise I'll use from to unwrap it and make it an observable. And then when we have that information, we'll get the finalized version of our options. And then we're going to call a function called extend, build or extend builder. This is something that we'll write in a moment. But what Extend Builder is going to do is going to take the default browser builder and then we're going to use some currying to take that result or that builder and then pass in our finalized options and our contexts and modify what the default browser builder is doing. So it's a little this is a little advance and there are different ways to do this. This is just one that I tend to prefer. So what does extend builder look like? Well, extend builder, like I said, takes a builder as an argument. And then it's going to return our options and a context and modify the options in context from that original builder. So here we are calling our builder, which in this case is the default browser build option browser built from that kit. We're going to pass in our normalized options. The original context. And then we're going to modify the third argument inside of the browser builder out or our transforms. Now, this is going to be our web figuration. And once we have access to this, we could just return the Web config and be happy. But we want to modify and add a new plug in. There's plenty of plugins out there from Let PEX ecosystem. The one I am a big fan of is the NION progress plugin, which will replace the standard out with a nice little Niam that. So with that altogether, minus imports, we're like forty one lines of code and we're extending the existing builder in a pretty non-destructive way. So let's take a look at what that actually does. So the setup here. If we look at the Andula Jason, again, we don't need this man builder. Here is our exten builder. We are passing in the building that we want to call. So bionic builders, extend, build and then the browser target here. It's just going to be our project name. And then the actual test that we want to execute. So let's go ahead and just run this real quick. So do Engy run in my lap? Exten built. And if everything goes well, we'll have a nice little Nyan cat coming across the screen and the our output was still work. So that's a little bit better than the default experience. And we get some Watpac goodness in there. That's pretty awesome. So, again, it's kind of wrap up real quick. That's where it near the end of our time. What if we learned? In most part, I hope, which you take away from it so that builders and cells aren't actually overly complicated. Again, that simple principle of. Here are some options. Do something with them. If not. And then return some results. That's really all you need to know that about builders and what they actually do. The hard part about creating builders is figuring out what API you need and how to create the flow. So once you get an idea of the certain API you need, you can piece together everything in a kind of trial by error. Process. Most of them are going to come from the angular dedicate package. And you're going to be reading a lot of type information from that. So I highly suggest, you know, look at the actual build angular browser or the browser builder from your death kit, slash, build angular. It's roughly a hundred and twenty lines of code. And that includes imports, utilities. And the most important thing about this is that there are types to follow. So if you get lost or aren't sure what to do, the type will guide you. There's also a lot of great examples out there in the ecosystem. Minko, if you go to his get hub, has a whole sample repo, basically rebuilding the command builder. And there is a collection of builders from this community called Angular Builders Slash Custom BIPAC. Which allows you to pass in a additional wet pack and have it merged into the internal web that can fit. Manfred obviously has his engy x bill plus, and then Angular Shuls has their selye deployed target builder for the plane to get Hupp pages all different ways that you can do this. And I'd be remiss if saying, look at the one that I wrote for Ionic Ionic Angular Toolkit. It has the same kind of concepts and actually uses that extend builder. Example. So at valuably, if builders are going to be something you need to know, honestly, most people do not need to know this information. If only just to understand how tools work, most people aren't going to be writing their own builders. But if you're in that use case where you need to include different tasks that are outside of ANGULAR, a builder can be a great way to do it. And there's going to be millions of ways to write the same filter. So don't screw it. What you saw here end up being the same as what you can.