Conf42 JavaScript 2022 - Online

Build your own Svelte

Video size:

Abstract

Inspired by talks such as build your own react, we are going to build our own Svelte. Svelte is a compiler-based frontend framework that compiles your code into optimised JavaScript. In this 40-min talk, we will write a Svelte compiler from scratch, and learn everything about designing a compiler.

Summary

  • Lee Hao: Today I'm going to talk about build your own svelte. Svelte allows you to describe your view as a function of data. Today we're going to learn how to create a spelt based framework like Astro.
  • Today we're going to talk about how you can create a compiler. It takes your code, parse it, analyze it, and finally generate an output. Let's look at an example of a counter component. Any feature that you see on here will be used.
  • Min: How do you pass a string into ast? Well, Min, all things in programming there's always a technique to do it. First of all, we need to describe how our code should look like. This is like the design document, like the PRD, describing how your syntax should look.
  • syntax of svelte is a fragments, which is made up of a fragment or multiple fragments without any space with the fragment. fragment itself can be a script, an element, or expressions or text. Now we're going to define the syntax of a script.
  • The core idea of a parser is to match the characters where I is currently pointing at. Based on the syntax rule we can decide whether to advance or to read the value out. We use a library called econt to import library for JavaScript.
  • To analyze our code, this is our JavaScript code. These are the variables that be top level and be able to be used in your template. We use a library called Esri Walker to traverse through the tree. How do I tell if there's multiple scope?
  • The idea of generating each code will be as simple as going through each node. Figure out what are the instructions that we need to insert for creating and destroying the elements. Then analyze and parse and then analyze our code.
  • We figure out how to write a compiler within a span of maybe 40, 50 minutes. All the codes that you saw just now is available on GitHub called Minisvelt. I have also recorded some of this talk and subsequent following up content in my YouTube channel. Do subscribe to his YouTube channel and I shall see you and hope you enjoy the rest of the conference.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
Jamaica real time feedback into the behavior of your distributed systems and observing changes exceptions errors in real time allows you to not only experiment with confidence, but respond instantly to get things working again. Code welcome to Conf 42 Javascript. Today I'm going to talk about build your own svelte, but before that, let me share by mama myself I'm Lee Hao again. I'm a frontend developer at Shopee. I'm based in Singapore, but I'm born and raised in Malaysia in a beautiful town called Bukit Murtajam. So outside of work I do a bit of open source. I'm a svelte maintainer, and if you'd like to follow me on social this is my Twitter handle, but most importantly this is my YouTube channel. I have a YouTube channel where I like to do contents about frontend that I wish it was there when I first started out. Well, when I first started out building web applications, probably eight, nine years ago, I started with jQuery. JQuery provides me utilities to select some elements and do something with them. For example, when user interacts with the page and the data changes, I use jquery to find the elements and modify them directly. But this gets messy quickly when applications get more and more complex. Fast forward a few more years I started to use React, which allows me to describe my view as a function of data. This declarative approach frees my mind to think about how to modify the elements, but let the react library to figure that out. The way how this works is that react takes a different snapshot of the virtual Dom, comparing it and figuring out the actions needed to update the actual dom. This comes with a cost, and because react has no idea what type of virtual dom there will be, whether it's this element, or that element, or this event handler, that event handler. So it has to have code to prepare for all eventuality. For a very simple application, the react library code may be overweight relative to the code you write, but this of course is a trade off. Your code is no longer as efficient as you would have it like if you write it in jQuery, but you gain the ability to describe as a more complex UI. But what if you can't write your component in a way decoratively, like how you would in react, but use a tool to turn it into optimized JavaScript code? That how you would write in jQuery. Well, that's when I started to learn about Svelte. Here's the svelte syntax. It's closely resemblance of the HTML syntax and you can define variables in a script text and use it in the HTML using the clear brackets. You can define CSS in a style tag to make changes of the variable. You can modify them directly like this. Here we add a click event listener called the decrement, which when we click on the button we'll call the decrement function, which will then modify the counter variable. And then you'll see that the counter value that we use in the curly brackets over here in a div will update immediately. Is that simple? But how does that work? A lot of time I hear this explanation where people say svelte is a compiler based framework. Takes a code. You write, analyze and compile it into JavaScript. So you can do a lot of magical things that you can do in JavaScript. But still, how does it work? I'm a big fan of the idea getting your hands dirty, learning by building. And I probably learn a lot from content like this. Say for example writing a library using jquery. Build your own react. But what about build your own svelte? I haven't find any content about that yet. So today we're going to going to do that and figure anything out together. I do realize that this may be the first time for some of you to hear about svelte. It's okay. I have a series of YouTube tutorials teaching about Svelte. You can visit them my YouTube channel, watch them later. But today, instead of talking about how to use svelte, I'm going to talk about how to create spelt, how to create a compiler based framework like spelt or quick or Astro, meta frameworks like Astro, and we're going to hear about them. And today I'm going to talk about how you can create a compiler. The word compiler sounds very scary to me when I first started to get figuring this out. Well, don't worry, we are here together to learn this min general this is a breakdown of what a compiler do. It takes your code, parse it, analyze it, and finally generate an output. Let's take a look closer look at each of this step. First, when we say the compiler process your code, it takes your code and generates a representation of your code that probably looks like a tree structure. And we call this an abstract syntax tree or ast for shots. It is called abstract because it contains the abstract structure of a code as compiler to concrete syntax tree, which contains all the concrete detail such as where is the semicolon, where is the parenthesis and things like that. So for abstract syntax tree just contains the semantic meaning of insight as a tree. And then the compiler analyzes this tree, the code through this tree. What this means is that irrecursively going through every code of your ast, trying to gather information such as maybe what are the variables being declared, what are the variables being used, and what variables will be changed and so forth. And armed with that information, the compiler is then able to generate a more optimized code. So today, with time permits, we're going to implement a very, very simplified version of spelt. This will be our compiles that we are going to compile. Any feature that you see on here will be used. Any feature that you don't see in this component will not be implemented. This is the example of the counter component that we just saw where you have two buttons, one click to decrement, one click to increment, and you have a div that contains the counter. Right? So here, let's take a look at my project setup. So here I have prepared with you with the code. Let me zoom that in a bit more. So here I have a app, dots felt, which is the counter component that you just saw. Sorry, I think. Let me clean this up a bit different. I think so. Okay, this is exactly the counter component that you just saw. And later on I will compile this app svelte into the app js file in this folder. Okay? And then you see the index HTML. This is where we are going to import this app js that we will be generating later on. And we're going to use this to create amount our component index JS is where our compiler code will be. So here I've already written the instructions, right. So we will read the file into this content, we're going to parse it into ast. We're going to analyze the AST to get the analysis. I'm going to use the AST and the analysis to generate a Javascript code. I'm going to write that file into app js in the end. So these three functions is what we're going to implement. So the first thing we're going to look at is this. Well we keep mentioning that the svelte compiler will compile this into an optimized Javascript. But how would that Javascript look like? Since we are creating a simplified version of svelte, let's design our simplified version of the svelte output. First, the simplest code for creating a button like this is to have these instructions where we create element going to add event listeners click and then the decrement function, we're going to append this button to the parent and to destroy. We are going to call the remove event listeners to clean up as well as remove child to remove this element from the parent. So here we need some function to call the crit and destroy instructions separately. So let's direct them in an object method like this one. And also we want to be able to create multiple counter compiles. So let's have it as a function that returns the lifecycle object so that we can keep calling app to create multiple instances of this component. And then we can call the create to mount and pass in the target where we are going to mount our component. Wait, oops, I think I forgot that the button needs to have some text. Right. So here I'm going to add a few more lines of code. Here we're going to create a text node. I'm going to append the text node into the button and we can keep doing this and this and this and we keep adding code to build up the component. Wait, but wait a minute. I think the counter expression is dynamic. So we need one more way to basically up call to updates the value updates the content of the text node. So we're going to create a lifecycle called updates where we're going to pass in a parameter called change which is taking min as an array that can contain what are the variables that will change and then we will change it by setting the data to the updated value. So here is how we're going to call this function. And the increment function will probably will insert a line called lifecycle update. The counter right here. We're going to probably will do some analysis to figure that out. Figure out if we're going to change the counter and if counter will change, we're going to figure out that where it will be changing and we insert the lifecycle updates method over there. That's why the analysis step is very important because it let us know to know which variables will change. Okay, but you may ask me one question. The way I visualize just now, right. We step through and visualize. This seems very straightforward from your naked eye. But how do you actually write the code to do that? Right, because in essence the code that you see the eye of a computer is just a bunch of characters. It's a string. So how do you take this string and then figure out what other elements are there? Well, that's why we need to parse the code into ast. That will be much clearer if you compare the steps using ast. So let me walk through the steps that we saw just now, but with the ast as we go down, we realize that actually traversing each element is like traversing through the each node in the ast. So that's why it's much simpler if you can able to write a parser to parse the code, right? So now that begs the question, how do you pass a string into ast? Well, Min, all things in programming there's always a technique to do it, and today I'm going to show you how. So first of all, before we do that, we need to describe how our code should look like. The way to describe it. We're going to use this thing called a syntax notation. This is like the design document, like the PRD, but describing how your syntax should look like. We need this before we can even start writing our parser. There are various syntax notation. It could be a real root diagrams. You can have the Becker's north form. Well, in this example of the Becker's north form I'm going to show you, it describes how a JSON object syntax should look like. So I guess as a JavaScript developer, you are very familiar with how a JSON object should look like, right? A JSON object could be either a open and curly brackets like this, or open brackets, a property list and a closed brackets, right? So the first one is for an empty object, this one is for object with some key values. But what's the syntax of property list? Well, we can define it. Again, the syntax of a property list can be a property or a property list with a comma and then a property. So you can see the recursiveness in terms of how you can define the syntax. So now what is the property? Well, the property can be a string and then a colon, the colon string, and then a value, right? And then you can keep doing this, define how you look like as a syntax of a string, and what's the syntax of the value? But that's not what we're going to do here today we're going to talk about syntax of svelte. So here's how I'm going to define the syntax of svelte. Say, syntax of svelte is a fragments, which is made up of a fragment or multiple fragments without any space with the fragment. So fragment itself can be a script, an element, or expressions or text. So now we're going to define the syntax of a script. Well, for the script is the brackets, angle brackets, S-C-R-I-P-T angle brackets, and then with some Javascript and then angle brackets again, S-C-R-I-P-T angle brackets. So this is a syntax for the script. How about elements? Well, elements is a angle brackets, the name of the tag, tag names, attribute lists and angle brackets, fragments, angle brackets, fragments, tag name and angle brackets. We can keep doing this for each of them, right? So for example, attributes is an attribute name with equal curly brackets and then Javascript and then curly brackets. Keep doing all this. Okay, so now after we have this, now it's time to implement our parser. Let me close this. Okay, so now let's write some basic structure for our parser. Going to type real fast over here. Wow. So the thing is, I'm going to here create a few helper functions I'm going to create a few helper functions for each type of the node that we are going to pass. So just now we see that we have fragments, fragment, Javascript element attribute. Yeah, so we're going to just create one function for each of the type, right? So here this structure, you see that we're going to create an ast, an object that represents the tree itself and call the past fragments to give us the ast. And here I'm going to define a variable called I. But what is I? So if you see I, I is actually a pointer. It will be a pointer that points to the character we have in the string. And the core idea of a parser is to match the characters where I is currently pointing at. Like here we are pointing min the angle brackets, and based on the syntax rule we can decide whether to advance or to read the value out from where I is pointing at. So we have our rules and we're going to figure out how to advance and match the characters based on where I is pointing at. So I have this page that shows you the syntax and let's get back to the code. So the first syntax that we see is fragments, which can be one fragment or multiple fragments. So before we actually go that, right, I think let's write some helper function for us to advance, match, advanced and read value. We're going to have one helper function for each. So to match the value I'm going to create the match function, which basically gives me a boolean value to tell me whether the I that's pointing at matches the value that we're going to call it. Right? So we're going to slice that string out and compare whether we match where I pointing at is matching the value. The string that we're going to pass min and the next one is trying to advance I, right? So I'm going to call this function called it. And the way how it works is that, for example, if I try to pass an attribute over here, for example, after I reach the attribute name, I know that the next thing I need to have is the equal and curly brackets. I try to match this, I will match this and if it matches then I will advance my eye, right? And if it does not match, then it's a syntax error, right? So basically we don't really care what symbol is this, we just want to match and then advance I and if it does not match, we're going to throw a syntax error and this is what exactly I'm doing, right, I've matched an advance. If not, I'm going to throw a syntax error, then lastly is trying to read the value out, right? So this case will be the case where we're going to use for reading value out, say the attribute name, reading the tag name or the attribute value, right? So here I'm going to create this function called read while matching. Basically it takes min a regular expressions, and as long as the regular expressions matches the content where I is pointing at, then we'll keep advancing I and then we're going to return whatever we have read so far while advancing I so the first thing is the fragments, right? So fragments is going to be an array and we are going to keep calling the parse fragments to read out the fragments and going to push it into the array. And this is the parse fragments. So for fragment itself we have seen here that it will be either a script element, expressions or text. So I'm going to rely on these functions to help me. Right, so you get a script element, expression or text. Well now let's go to the more difficult one, right, script, how do you pass a Javascript? Let's take a look. So for script we're going to match this string, right, SdRI PT with angle brackets, and I'm going to keep reading anything until we hit the angle bracket sri pt so this is how we're going to parse the Javascript. So here I'm going to match script and then I'm going to edit this so that we can advance, and then later on it will just keep reading contents until we find the angle brackets. SRi pt so this is the code that we're going to take it out. And of course today I'm not going to spend my time to figure out how we can write a JavaScript parcel. We can actually use a library to do that for me. So I'm going to use import set library, which is called econt. Okay, I'm going to parse it and then I'm going to advance my eye and also eat away the closing JavaScript tags. And that's it. So for parse element, let's see. So for parse element we're going to have angle brackets, the tag name attribute list, angle brackets and then pass fragments, again, angle bracket slash and then eat away this tag name, right? So it's like matching read while matching parse attribute, list it, this pass fragments at the angle bracket, slash it, the tag name as well as angle brackets. Let's try to do that. Right. First we're going to match edit and I'm going to read while matching and I'm going to pass and then yeah, so basically how we see the syntax rule, how we translate it and how we just type it out. So for children we're going to call past fragments, but the past fragments that we have over here will keep reading until we hit the end of the contents. But the past fragment here I'm going to use is until we hit the closing tag, right? So maybe we want to have a different condition. So we're going to create a parameter called condition, replace it, and so here we're going to keep matching until we'll match the end tag, right? So here on top we're going to just keep reading until we hit the end of the content. So we have passed elements, let's go to pass attribute list. This is kind of similar to pass fragments. So going to have an attribute array and then skip white space, right? So in each attribute we're going to have one white space. So the white space can be more one character white space, multiple character white space. So how we can do that is actually we're going to reuse some of the utilities that we already have, like the read while matching, basically skipping white space will be like reading white spaces character, but then we just ignore whatever we read out so far. Okay, so now, next thing. So now we're going to keep reading attribute until we hit the angle brackets, which tells us that we are at the end of this opening tag. So while we are not matching, we're going to call pass attributes and then we're going to keep looping that. So for pass attributes here, if you see, we will be keep reading whatever it is until it hits the equal sign and then we're going to eat these two characters until it hits the color brackets and then we're going to eat the color brackets so here I'm going to read while matching, type out real quick, right, it's it. And so for pass expressions, if you see here, we're going to eat curly brackets, some JavaScript and curly brackets. So we're going to match curly brackets, eat that, parse JavaScript and it another closing curly brackets for text. Basically it will be anything that is not script elements or expressions. So basically we can keep reading any value until we hit the angle brackets or the curly brackets. So we're going to use the read while matching, and then we're going to trim away some of the empty spaces. So if you take a look over here, the code from here to here, there are some new lines, characters, but we're just going to make things easy. We're just going to skip and don't see them. So as long as the text is maybe after we trim is just content full of anti strings, then white spaces, then we're just going to ignore it and only have the code for text that has actual values. So finally we're going to call Parse Javascript here, we're going to use acon to help us to do that. Okay? And I think that's it. Right, so let's see our result. So here I'm going to call my, hold on, let me see here we can pass, and then since we ever have the analysis and generate, let's comment them out. So I'm going to write the code, the ast that we have into a JSON file est. Okay, I'm going to run this. And here we see basically our est that we do. Yay. Okay, so here we have our, let's collapse this. Here we have our HTML, which is the ast of the template, right? So here if you look side by side, you will see that we have the elements, which is the button over here, and then you have attributes, which contains name on click and the value which is JavaScript est. And then next we have the children, which is the text code. It's called decrement. So next one we have this element called div children. And yeah, basically we have everything now. Okay, so I think that's it for parsing. Yay. It's not that hard, right? You can do that. So I think let's continue when writing the next part, which is to do analysis. So to analyze our code, this is our JavaScript code. If you just look at it, you probably can tell that there will be three variables, right? These are the variables that be top level and be able to be used in your template. And you also can tell from the eye because I believe you are experienced developer can probably tell that here we are modifying counter, right, because the variable names is the same, but it's not that straightforward. It's not just about variable names because if you look at the code right here you can probably say that, yeah, you're right, because although the counter variable has the same variable name, but this is modifying the counter being declared locally and not modifying the counter variable up there, right? And this is because of this concept called a shopee, right. Each of this scope you can declare a variable, define a variable, and then if you are assessing the variable, you'll be looking from the inner scope before we look up to the outer scope, right? So here there is three shopee, but you may ask me how do I know, how do I tell from Li Hau Tan tell there's multiple scope, but how do you able to tell that with your code? Well, it will be much easier if you have the ast, right? So this is the ast for the Javascript and you try to color code the scope, you'll see that a scope is like a subtree of your est. So the root of that subtree is a node that creates a new scope. And today we're not going to figure out how to write the code to analyze the shopee. I'm going to use a library called periscopic to do that for us. Okay, so I'm going to import this library. No, I think let me walk through one more. So we're going to have this periscopic. What it does is that you can analyze the JavaScript est and it returns you the map, the globals and the scope. Okay, so the scope is the root shopee and then the map is actually a mapping of the code that creates scope and the scope itself. So in this case if you have three code, then the map will have three entries, right? The keys will be this code, this node and this node, and then the value will be the scope that the root scope, the two child scope that it creates, the scope one and scope two. Okay, so with that I think we probably should start writing some code in our analysis. So here, let's figure out some structure. First is what we're going to analyze. So we're going to have the object called results. I'm going to store things like what we're going to analyze over here. So we have three things, right? The variables, what are variables and what are the variables will change and what are the variables will be used in a template. So the first thing is that analysis. Are we going to call it periscopic to analyze the scope for me? So here, based on what is defined in the root scope, we can declare in root scope we can tell that these are the variables that you can use, right? And then we're going to keep the root scope and the map in the results so that we can use them later on as well. So we need to figure out what is the current scope. I'm going to use a library to then traverse through the tree. This library called Esri Walker, it has two callbacks. One is the enter we will call whenever we enter a node and then we will call whenever we are leaving the node. So how this Esri Walker works is that it uses the daffer search. So it will traverse the tree in a daffer search manner. And you see that we're going to figure out what is the scope, right? And because of this deferred search manner, we can't actually able to tell what is the current scope because whenever it looks, does the deferred search traversal whenever it enters its node that is creating a new shopee. We can find this by looking at the map that returns from the periscopic. Whenever we found a map we say okay, now this is a node that creates scope. Then we'll set the current scope as the scope that this code creates. And then we step through, therefore search and keep traversing through the inner child nodes. And then as we come back up and we encounter this node again, realize that, okay, now we're exiting this scope, we're going back to the parent scope, right? So when we exit, we'll reset back this current scope to the parent scope, right? So here whenever we encounter a node that has in the map, then we can set the current shopee as the scope that we're being created by the nodes. And if we encounter a node when we're leaving, then we update the current scope as the parent scope. So now with the scope, now we can know, now we can find the variables, whether the variables is being declared in the scope. We can use the current scope to figure out where this variable is being declared. So here we're going to check, I think first is we need to figure out what are the code that will make changes, right? So here we use a tool called the est Explorer. I just pasted the code in here so you can see over here, if I hover over here, that you will see that this is an update expressions and then the variable itself is in the argument name. This is the variable name. Okay, so we need to check if the node type is update expressions and we ask the current scope, which scope that we declare this argument name, this variable name. And if this is being declared in the root scope, then we know we encounter something that's changed. Then we're going to say result will change. We'll add this thing in. Okay, and I think that's it for analyzing what variables will change. The next thing I want to analyze also is what are the variables that will be used in a template. So here what we can do is we're going to traverse through the ast that we just created, the fragments. And then over here we are going to look at two things, right? So first is that if it's being used as the attribute value, then this is the variable that we just assume this is being used in a template. And over here also if this variable is in an expression, we are going to say it's being used in a template. So encounter, depending on which type of the frank type we encounter, if it's an element we just keep traversing its children and its attribute. And if it's an attribute, then we're going to say attribute value is being used in a template. And if it's expression, I'm going to say the expression value itself will be used in the templates. And I think that is it for analyzing. Let's try and run our code. This time around I'm going to comment way analysis and generate MJ's console out analysis. So let me run the code and you'll see this is the analysis results on the variables change and be used in templates. And if I come over here and try and change something, for example, if I remove this too, just completely remove it, then you'll see that, hold on. Then you'll see that variable that we use in the template is only decrement, right? And if I try to come over here and say, for example, remove this variable, then you'll see that the variables we have left is counter and decrement. And over here if I try to say let counter equals to zero, then we know that this counter is actually referring to discounter rather than outer one. And run analysis again. You see that world change is now an empty set. We didn't change any variables. Okay, so let me reset this back and then I think it's time to go back to our code. So now we're able to analyze and parse and then analyze our code. I think the next thing is we're going to figure out how we can generate the code. So the idea of generating each code will be as simple as going through each node and then figure out what are the instructions that we need to insert for creating and destroying the elements. So we're going to go through each of the element and figure out what are the instructions needed. So let's go back to our code. So here I'm going to write something, right? So for generate, going to create a few arrays which this will containing the instructions for create, update and destroy, and then also the list of variables that we use. So here the code will look something like this. So here, min, create, update, destroy. We're going to join the instructions over min, each of them and then also for these variables that we have, so need to declare them. So here is how I'm going to declare using the let the variable name. Okay, so here if I try to run this now, you'll see that I have a very basic structure. Now we have nothing. So now let's continue to write our code. So here I'm going to create a function called traverse. I'm going to traverse each type of the node. So for element, what we have here is we have instructions to create element and then we also call traverse for the attributes and the children. And then we need to append a child to the parent as well as to remove child in the parent in the destroy methods, right? So these are the things, the next element we're going to do is the text. So for text, if you look here, next thing for text we want to need is to document a create text node for the text content and then we append to the parent. Okay, so here we're going to create the text node, we're going to push this variable name and then we're going to say we're going to create text code and then we're going to use to append the parent with the append child. Next thing we have is attributes. So for attributes we are going to just handle the on click event listeners. So for that we're going to have two instructions, one in a create array when instructions to add event listeners the events name and the variable. And then for destroy instructions we're going to call remove event listeners the name of the event and then the variable. So this decrement comes directly from the attribute value and then the value of the listener compiles directly from the attribute name where we just remove the first three characters. Okay, so here I'm going to do that if it starts with the on event listeners. So we're going to get event name right, remove the first three characters, event handler. We just assume that the variable itself is an event handler and then we're going to basically add instruction to create and into destroy and that's it. So lastly we have expressions. So expressions like this, we're going to just treat it as if it is just a text note. And then one thing we're going to do is we're going to analyze and we're going to use analysis to figure out whether this variable like the counter will change. If it will. Then we're going to include these conditions here to basically call to update the text value with the latest value. So here I am going to create this text code first, right. And then I'm going to use the analysis basically to check if we have the world change. Then we are going to new instructions in the updates call. Okay, I think that's it, right. So I think we will have one more thing which is to call traverse the fragment, right. So let's save this and let's try and run our code. So here is our code right now, okay. Basically we have all the structures, it looks quite good, really you should proud of yourself to be progressing so far. So I think one thing we're still missing is that this variable like decrements, these are not defined yet, right. Basically the code inside this Javascript over here, we still haven't figured a way to insert them, right. So we need to figure a way to basically add this lifecycle update over here in all the update expressions. So to do that we are going to paste in the code that we have earlier on, basically is where we use the, where we try to figure any variables that will change. Right over here I'm going to make some changes. Basically we are going to also check whether the variable, not just we know that it will change, we'll also check whether it will actually be used in a template. If not, we're not going to add the lifecycle updates function, right? So if we have this used in templates then we are going to insert this instruction for lifecycle updates. Okay? Yeah, this one, just assume that it works. I'm not going to explain too much into this one. So by now let's see, I think we need to, after we change the ast, we need to come over here and insert this code. So we're going to use a library called escode gen, basically what turns Javascript into a string. And I'm going to turn that ast into the string since the whole thing we generate is a string. Right. So we're going to turn that into a string and then insert a line over here and I think that's kind of it. Right. Let's try and run our code and see what we get. Let's fresh this. So here we basically have this instructions, looks promising and we have the lifecycle updates. And in the lifecycle update you have this. And let's try and run this in a browser, shall we? So here I'm going to start the server, I'm going to run this server and this is the compiles that you see, right. And this is the magical moment. I'm going to click on the decrements and it works. Yay. And increments, yes, it works as well. Whoa. Okay, so we kind of managed to write all this within a short period of time. And so you see that this is how a compiler is being written, right? And to show you, to demonstrate to you that this compiler writes is an optimized, generates an optimized javascript code and actually uses what we analyze, let's try to make some changes in our component and see what is being generated. The first thing is that what if I do not change anything in the counter, right. For example, we have a variable called counter right here and we try and modify the local variable. So basically this counter is not going to change what will happen trying to build this. You will see that first of all, I do not insert the lifecycle update, and secondly, in the lifecycle update method here is empty. So we don't need to generate extra code. If we don't really need it, right? We are not going to change the counter, then we don't have to need the update function, right? Of course you can do it much better by removing this function totally because you don't even need it. Yeah, we should do that. So maybe you can try doing that yourself. Right? So what if, say for example, we're not changing it this way, but we are not going to have this counter variable, right? We can still click on counter, counter increment and decrement, but we're not going to show that on the template. We're going to use the counterparble in the template. Guess what happens? This is what happens. So first of all, again, we don't have that Div and the updates, we don't have the instructions to do those things. And secondly, this knows that we can still update the counter variable and we can still console log to see the updated value. But we no longer needed to call the lifecycle updates because we are not using that value in the template. So there's no need to call the updates at all because there's no dynamic expressions over here. So you can see that we are actually create an optimized Javascript based on what we analyze. And basically that's what svelte do. Okay, so some summary. We figure out how to write a compiler within a span of, I think, I don't know how long we have spent, maybe 40, 50 minutes. Right? So if you can type really fast, that's how long you need to take to write a compiler. Right, so what is a compiler? This is the compilation step. We parse our code into an ast and then we do some analyzing to get an analysis, and then we use the analysis and ast to generate the JavaScript codes. Right. So here of course we don't have time or we don't really have a space for do questions, but feel free to find me on Twitter or on YouTube. So this is my YouTube channel and all the codes that you saw just now is available on GitHub called Minisvelt. Here you can find this. And actually I have also actually recorded some of this talk, some part of this content and subsequent following up content in my YouTube channel. You can go and watch that. It will be part of this build your own svelte series hold on, series where you will see that you'll find a series. Hold on, let me get the playlist for you. Right, so this is the build your own svelte playlist. Right now we have three and I intend to add a few more follow ups on how you can make it it more complete, how you can add reactive declarations that you have min spelt, how you can do it on the server side rendering and things like that. So do subscribe to my YouTube channel and I shall see you and hope you enjoy the rest of the conference. Bye.
...

Li Hau Tan

Senior Expert Engineer @ Shopee

Li Hau Tan's LinkedIn account Li Hau Tan's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways