Creating My First Open Source Vue 3 Library

Bookmark

Learn about how to create a Vue.js library, and what best practices you should follow. We'll also learn about open source, and how maintainers work. We'll touch on xState, the composition API and testing strategies including Cypess and Vue test utils!



Transcription


Let's talk about vue 3 and creating your first open source library. So there's a few goals that I want to have for this talk today. We're going to talk about design choices and requirements, and I'm going to talk about a personal example of how I created a vue 3 open source library in the last couple of years. We're going to talk about community and open source and how to deal with that. We're going to look at some lessons learned that I've had through this whole process, things that I would have done differently and things that went really well. And then I'll have a conclusion and some things you can take away that if you're going to create an open source project that you may want to take into consideration. So let's do a little thought experiment before we get too far. Let's imagine you want to create a vue 3 library. Say you're on a team, you have some utilities, you have something you want to open source and put into the wild that anybody can contribute with. What are some things you need to think about as you create this open source library and what things do you need to take care of? So essentially, let's think about how should you build it. So there's a couple of ways we can build modern vue 3 applications today. We do have vue CLI, although it's not recommended, but you can use it. And there is vite. There's javascript or typescript. There's the Options api or the Composition api. There's Pina or XState. And then there's JSX. So one thing to keep in mind is that even though you're using vue 3, which often uses these templates, you can, since you are using an open source library, use something like JSX because a lot of other people know JSX. If you're coming from react or another framework that uses it, it's pretty popular. And even vue Defi uses it in its open source library. So you have to kind of make some of these decisions. So at the end of the day, the right answer is not always obvious. It's not really always obvious of what is the right thing or the wrong thing to do. And you have to make some compromises. And it may not be exactly the correct answer at the end of the day. So what I'm going to show you now is a project that I worked on in the last two years as an Amplify UI team member. And I'll tell you some of the decisions that we made and how it went. So who am I? I am a developer at aws working on the Amplify open source libraries. You may have seen me online. I have a YouTube channel called Program with Eric at Eric.video. I've also written some blog posts. And I'm also a developer advocate. So recently I have moved roles to being a DA, which has been amazing since I get to talk to so many developers and really talk about Amplify and all the tools that it has. I like to start the talk with this XKCD comic that talks about how to write good code. You can see at the top, you see start project, do things right or do them fast. And then if you go fast, does it work yet? Or if you do things right, do you code well? Are they done yet? But at the bottom, this often happens, is you throw out all your work and start over again. So as soon as you create code in any sort of project, after you're quote unquote done, it becomes something that's a legacy app that needs to be maintained. And what happens is a year, two years pass by, new teams come in, new people come in, the technology's old, it often gets rewritten. And that's actually similar to something that happened with this project that I'm going to talk to you about. So the My Project that I want to mention in this talk is this is what I fondly describe as the authenticator, an authentication system. It's a widget, it's sort of like a component that you can add into your application that gives you authentication so you can log into an app, you can sign into, you can create a new account. But it also has authorization. So it's backed by Cognito, so you can protect your routes for only authorized users. It's just a minimum amount of boilerplate code that you add to your application to add in an authorization authentication system. And it's completely customizable, supports all sorts of things like federated logins with Google or Amazon. And it also supports multi-factor authentication. So it was quite a big project to take on. Now we did have two prior versions of this authenticator in the wild. So it was originally written, I believe, around in 2019, where it was using a really old version of vue 2. It had then been rewritten to use web components and using Stencil. And this was our third round of rewriting this application, this component, to be more user-friendly. We found that using web components and Stencil had a lot of interesting problems with especially password managers and the way the DOM works and ran into some customization issues. So we decided to rewrite this web component, which was actually working not just for vue but react and angular, and rewrite it also all in the native libraries that they support. So we rewrote the authenticator using vue 3. And I'll show you how that worked. So as this process was going on, I started the team, like I said, it happened around a couple of years ago. We came up with a list of requirements that we wanted to make sure we hit. So we had these requirements. First and foremost, accessibility was extremely important, and we'll get into that. We wanted to make sure we also had a small bundle size. One thing that happens often with open source projects, you put them into your application, then you notice that your bundle size doubles in size or triples in size. And we wanted to make sure that we can make it as small as possible. We also, since we were rewriting the vue authenticator, we wanted to make sure that we also included code for react and angular, and that we were able to reuse code. So we'll talk a little bit more about that. We used yarn workspaces with a monorepo, and then we used a different type of way of sharing code. And then we wanted to have some vue 3 best practices. So this vue 3 was extremely new when we started it. And the best practices were starting to be known, but it wasn't as clear as it is today. And then of course, we wanted our app to be tested. And we had a couple of testing strategies. So as I mentioned, accessibility was one of our number one requirements. We wanted to make sure that we were WCAG 2.1 AA tested against. We had a number of accessibility experts on our team and other teams that helped test. We ran some accessibility tools against it. We made sure that anybody using different types of screen readers or different types of applications, different types of use would make sure that our component that if they added into the application would meet that high standard. And then for bundle size, we looked at vue CLI, but we decided to go with vue. vue was pretty new a few years ago. So we really liked vue and the rollup and how it was creating its bundle size and how small the bundle sizes were. We made sure that we had it all ESM compatible for our whole application, of course. And we reduced the number of dependencies. So we looked at things like lodash and moment.js and some of those other libraries that are often common that you might add into your web application. We removed as many as we can. So that way that we wrote all those functions ourselves that we didn't have those libraries increasing the bundle size by however much. Now the sharing code strategy that we had, you can see here at the top, we use this kind of this aws Amplify UI parent component library that encompassed X state and some utility functions. So we'll talk a little bit more about X state, but that is our state management system that we went with. And then our packages for our Amplify UI vue, UI react, and UI angular also had a dependency on Amplify UI internal package. So that way they all could share code. So as we were writing all three of these at the same time, we took different pieces of code that we found that were common between all three and then pulled them back up into the Amplify UI. By the way, this is all open source. You can check out our repo if you want to get a little bit more details of what that looks like today. It has changed a little bit, but it's mostly the same way. Now I talked about X state and X state is a finite state machine library that allows you to share code in your application. So we really liked the idea of finite state machines. We looked at vuex and Pina, which is really well known in the vue 3 ecosystem, but we thought that this state machine made more sense. It had libraries for vue, react, and angular, and we were able to share that logic between the same state machine between all the different implementations of the authenticator. It also had great package support when we ever had any questions or issues. We had really good support from, I believe, David K. Piano, and it looked just really interesting. So it's just an interesting project and interesting way of handling it. So if we went through something like Pina, we would have to figure out how to get that working with vue or with angular and react, and that would probably be a lot more work than it's worth, while X state just worked out of the box. Now for the vue 3 best practices, this wasn't as well known when we started, but we definitely went all in the composition api. We started with the composition api with the setup function, if you remember that, but we quickly moved over to the script setup, and then script setup with language equals TS. We had single file components with templates. We looked at the JSX route, but since I just wanted to get started quickly and I thought it'd be easiest, I started with templates. We used typescript. We turned Strict turned on. We have Strict mode turned on in our TS config. We did have any turned allowed, so it wasn't as high of a typescript as we probably should have had, but we definitely kept it with that level. We did ES linting. We did the vue 3 essential. That's a level when you add in linting with vue 3. So that gave us some pretty good common recommendations when creating a vue 3 app of how we should style it. We made sure that if there was any ES lint errors, that it would prevent compiling. And then we used a lot of stuff that for vue apps, you should probably use. We use slots, we use V models, and we use emits. So if you use the react application, you probably have seen where functions get passed down as props, but we're always like, this is part of vue. Why not just emit data back up? So we use that pattern quite often. So we wanted to make sure that we weren't just creating a vue library that used react idioms. We wanted to make sure they were separate. And for testing, we had a couple of different choices. So we used vue test utils with testing library. We think testing library really promotes accessibility and ways of creating your app. So we looked at V-test, but it came a little bit too late into our process, and we really wanted to keep with Jest. So we stuck with vue test utils. We ended up using cypress end-to-end tests throughout all our applications. It was our main way of testing. And then we used Cucumber, a library to use behavior-driven development. And I'll show you what that looks like. And then we went all in on github actions. So all our tests ran on every single PR. We created a bunch of Actions to help us publish each one of our packages. We also ran a Canary. So we thought this was pretty novel for open source work. We ran a Canary that takes vue, CLI, V2, V3, and also the same for react and angular. And we run this test every 15 minutes where it creates an application, uses our library, builds it, makes sure there's no errors, and then it runs a cypress end-to-end test to make sure that you can log in. So we run this every 15 minutes that way, and it runs on our latest. So that way we know that if we push up a new commit to our repository that breaks anything, our Canaries will break first. And the whole idea was to make sure that we're the first people to know if we broke anything in our library. And that's worked out really well. So here's a little bit of how we did our cypress Cucumber testing. So here's a scenario. Sign in with unknown credentials. So this is a very human-readable way of writing your tests. These all get translated down to cypress functions which run things. So when I say, when I type my email, status unknown, the email and unknown are aliases that get passed into a cypress function, and then it executes the code. And I type my password, I click the sign in button, and then I see user doesn't exist. So we had this throughout our application, and we pretty much tested everything that we could using the cypress end-to-end tests, using this Cucumber. So let's talk about community. So we wanted to, since we are an open source tool, we wanted to obviously use open source tooling in our app. We used yarn Workspaces. We had a debate between yarn Workspaces and NX and a few others, and yarn Workspaces just won out. And we've really loved it with our Monorepo. And then we used Next with MDX for our documentation. And so as this talk suggests, we definitely open sourced the repository nearly from the beginning. We didn't really have any closed private repository at any time. Like basically the initial commit was on an open source repository. We did, since we were, since there were older versions of this authenticator component, we did create an RFC where we asked the community for feedback on this new approach and being able to rewrite our whole application. And we got some good feedback there. We did a developer preview. So as we kept moving forward, we did tagged releases on GitHub for developer preview. So if anybody wanted to check it out, they could. And that really helped out at the beginning. And the whole open source community is awesome at trying these things out in our community for sure. We created an office hours on Discord. So we have a whole Amplify Discord channel. And in that channel, weekly, we would have office hours where anybody can ask us questions. We would give updates on our progress. And it would actually be all through the whole Amplify ecosystem because Amplify is not just this authenticator, but it's also a set of like a javascript library. It has some hosting and a few other things. So we had that all open. And then we pulled every single GitHub issue that we had from the previous versions of the library to help us really understand what the problems were with the older versions of the libraries, what people liked, what people didn't like. And that helped really guide the requirements that I mentioned earlier and also made sure that we knew when we were done, when we had those issues fixed. This is an interesting idea we did through our development process is that we did documentation driven development. So what is documentation driven development? As we were going through our feature list, we documented the features first. So we would go in, we would document them inside our doc site, and then we would implement them. And then at the same time, as we were implementing it, we would add unit tests or more often end to end tests with a sample application that ran in cypress that described the doc. So that way, as we were writing, we knew that the docs we wrote worked. We knew that the test we wrote to those docs that wrote the specifications that worked as well. Really, docs were a first class citizen. We did a lot of time testing different documentation platforms. We tried Next, we tried some of the view platforms we decided to stick with Next. And then we made sure that inside our docs platform that we had embedded our authenticator. So it was running in real time, but maybe it also mocked some of the backend. So at least you can test it out and run it. And so that worked well. We had to, since we use Next, we ended up using the react authenticator in the documentation for all the examples. But we found out at the end of the process, the look and feel for customers between the react, angular and vue versions were exact. They were identical. So it wasn't that big of an issue of using the react authenticator in our docs to describe some vue features because they were all in parity. So this is what the docs website is. We looked at the docs from other Amplify and aws sites, and we decided to be a little different. So we wanted to emphasize our accessibility, our connected components, because the authenticator connects to our aws services like Cognito, like I mentioned. So we kind of came up with this idea where Amplify uses collection accessible theme, will perform at react and more. And then we had a little code snippet since all our libraries were on npm, and then you can install it. And then to highlight how small the amount of code is to add this in, here's an example of how you do this in vue. You just inside your template, you would add the authenticator, and then this would produce this whole authenticator here with the sign in and create account. And we would give you a ways that if you created a different backend system in Cognito, let's say you add a phone number or a username, or you had certain signup questions, those will automatically be configured and taken from the authenticator. So you wouldn't have to configure it yourself. So that made this process much easier. And then we added in other abilities and definitely lots of different things from moving the create account tab for some people, adding customizations at the top, the footers and headers of this. So we try to add a lot of different customization for a lot of different styles and allow customers to override the look and feel of that. So this whole process took about one year to get the application, this authenticator written in vue and out as a developer preview and a couple of months before we got it into general availability for everyone. So here's a few things we learned throughout that whole process. So as I alluded to before, the parity between vue, react and angular was awesome. At the end of the day, all three shared a lot of different code. We shared some styling and they looked nearly identical. And the typescript really ended up speeding up development. As I said before, we had strict turn on, but we did allow any. So that wasn't perfect, but just having typescript was a blessing and we were glad we did it. And we now even have higher levels of typescript and support now. And testing, we heavily went in on end-to-end testing with cypress and that caught so many issues, especially during development. We would be working really closely on a new feature. We would push it up, our end-to-end tests would run, they would fail. We'd know we had an issue, we'd fix it. And I think we caught a lot of issues with that. And we had a lot of positive customer feedback. So a lot of people who use the older versions of our library and authenticator came to the new one, really enjoyed it. Glad it was native for vue and the bundle size improvements and the speed increases were all awesome. But there with every story, there's things that we could have done better. We did have that vue 3 recommended, but we should have spent more time on setting ESLint levels, setting custom ESLint levels, probably having more... We probably should have had more coverage in that area and maybe had coverage reports and a minimum amount of testing before things would pass. Something that we added in later, but during the development we didn't have in. We were okay with the ESLint level, but definitely we could have had it higher. At the beginning of the project, we were thinking about supporting vue 2 because this was during the time that a lot of people were transitioning, maybe still is to this day from vue 2 to vue 3. But libraries like Vue2me and a few others weren't as popular as they are now. And I don't even think they existed. So it would be really difficult to keep the vue 2 support in the new library. We still supported the older library for vue 2 users. We do too as of today, even though it's being maintained, but no updates are being added to it. But that's something we could have probably done different. More JSX. So I found as I was working on the project, there's a lot of people on my team that were JSX experts, and this could have been something where we've added that more in. And we're exploring that as we add updates to it if we want to rewrite some of our templates in JSX, because it's just gotten really, really well. And an open source library, it shouldn't matter. I mean, obviously our end users are still going to use this in templates. It's not going to affect that at all. It's just the way the sausage is made, I suppose. We had lots of slots within slots. We had a very complicated nesting system that the idea was you could override any function that from a deeply nested component. Looking back on that code, definitely could have been simpler. Maybe we should have used provide and inject. Maybe we should have used it in some kind of state management system. I think that could be improved. And as we've gone through, we have improved a lot of that. And we did overemphasize end-to-end. End-to-end is amazing because it's almost like as if the user is clicking on different parts of the app, typing things in. You're very thorough in your testing. The downside of it is it's really slow and it's hard to iterate quickly. And we didn't do as many unit tests as we could have. So I think we should have had a better balance between unit tests and end-to-end tests. I think that would have covered more. At this point, though, we do have a minimum amount of unit test coverage that we require now on every single feature. So that has been corrected. But during that process, we probably should have done more unit tests. And this is a problem that we're still trying to this day. We're still trying to get better at is how do you get external contributors to go into your library to be part of the community, to comment on RFCs and issues, be able to take up problems that community is having, maybe working on features. That is something we're still working on. So if you go to our Amplify UI components library and look at our list of issues, there's a lot in there. Not a lot, but there's definitely ones in there that the community can work on. But trying to get the community to work on it, we've tried to add tags. It's just difficult. Plus, we have a roadmap of things that we're working on for the next year. And that's something like the public doesn't have all the information on. So it's hard for someone to implement a feature that we're going to be implementing in three months. So we're still trying to get better at that. That's something that we're learning about. So let's talk a little bit how it went. So we've looked at this whole process throughout the two years, what went well, what didn't, but what did we end up with? Well, a good indication of how any open source project is doing beyond looking at the issues, comments, and mentions on Twitter is just to look at the npm downloads. This is a graph from npm trends. And so we released the aws Amplify slash UI view library. It was hovering around probably between five to 10,000 for quite a while. But as we started putting more effort into it and we created this new library, you can see it's almost doubled in the amount of downloads in a year, which has been really awesome. These are weekly downloads. So we went from 10,000 weekly to 20,000 weekly, and it's gone up from there. And it kind of goes up and down depending on the season and the time, but we're really happy that people are using it. And this same sort of graph you can see in the UI react and UI angular. react has definitely had more downloads, but it's also increased about the same amount. So need more info? You can tweet me at EricCH. Feel free to check out the aws Amplify U repo if you have a second. I appreciate everybody for listening in. I'll be available. Thanks. Thanks for watching.
27 min
15 May, 2023

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic