Contributing to Open-Source - A beginner's guide
Tags: opensource, javascript, vue, webdev
Published: 2020-04-09 16:13:34

Before we begin: The "beginner" in the title is for open-source newbies. The article assumes some level of front-end knowledge; having said that, I'd encourage you to read-on nonetheless 🙂

---
**What's going to be covered in this article?**

This article aims to be a simplistic guide for people who want to get started with open-source contributions; it's to help newbies navigate through issues, the code base, etc.

We're going to,

- Pick and Fork a repository (repo)
- Pick an issue to solve
- Build a fix (and refactor it) 
- Raise a pull request (PR)


**Why am I writing this?**

When I googled "How to get started with open-source contributions", I didn't find any solid how-to guides. Most articles or tips I could find were centered around making documentation changes for Hactoberfest, while those are great and they give you a good sense of how to get started with Github and PRs, they still did not offer enough of a platform to build on.

---
I've picked Vue as an example, but you could equate these steps to most open-source repositories.

---
### Step 1 - The Issue

The first thing you'd want to do is go to the Github repo for VueJS, go to the issues tab and filter by label - "good first issue" and look for PRs that don't have the "has PR" label.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/37mb898cm4hjmunnm7hf.png)

*Note: These labels are pretty standard but they're not guaranteed to be the same if you're looking at a different project.*

In the list of open "good first issues", we're picking the second one titled: 

[$http.delete shows warning "avoid using JavaScript unary operator as property name"](https://github.com/vuejs/vue/issues/11090)

When you open the issue you'll find the following details,

> Version
> 2.6.11

> Reproduction link
> https://codepen.io/frOracle/pen/vYOEEVW

> Steps to reproduce
> use $http.delete in @click

> What is expected?
> $http.delete is a function, not unary operator

> What is actually happening?
> a warning "avoid using JavaScript unary operator as property name"

*Note: Issue logging is usually driven by a template that's decided by the project authors.* 

In this case Vue has decided that they want the person logging the issue to

1. Provide a reproduction link
2. Provide steps to reproduce

and answer a few questions around the behavior.

If you open the codepen link and open the console you'll see the following error,

```html
"[Vue warn]: Error compiling template:

avoid using JavaScript unary operator as property name: 'delete()' in expression @click.prevent='$http.delete('/rest/close')'

1  |  <div id='app'>
2  |  <a href='#' @click.prevent='$http.delete('/rest/close')'>click</a>
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3  |  </div>

(found in <Root>)"
```

Let's try to understand what's happening here; based on the issue description, the expectation is that Vue does not throw a warning when `$http.delete` is used as delete is a valid HTTP verb (others being GET, POST, PUT, PATCH).

Come to think of it, Vue shouldn't throw a warning when any form of `.delete` is used, either with `$http` or any other object as `delete` is a valid property name on objects. You can read more about [property name limitations](https://javascript.info/object#property-names-limitations).

---
### Step 2 - The Solution

So before we get to understanding the project structure and where we can make this change; this issue (and not all issues can be solved in this manner) can be solved as a generic JavaScript problem statement.

To solve this issue, we essentially need to check a string and not throw a warning in case `delete()` (or any other unary operator) was invoked as an object property.

> For given string `str`,
> We need to return a truthy value if the `str` has a `.` prefixing the `delete` keyword

The simplest way to solve this for the most basic case would be,

```javascript
let unaryOp = 'delete';
let str = '.delete';

if (str.charAt[0] === '.' && str.match('delete')) {
  // continue and DO NOT throw a warning
}
```

Now the above code is in no way ideal for a variety of reasons, some being:

1. It caters to only the `delete` operator; we need to also take care of `typeof` and `void` for instance.

2. It assumes the string `str` would always be of a particular format.

3. Works only for `.` as a prefix; the logic is not flexible enough to add another prefix.

We need to write this in a more robust manner to cater to the problems mentioned above - as a rule of thumb (and not just for open-source projects), challenge or question the code you've written, because almost always, there's a better way to write it.

We can use the [RegExp](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) and the [string match method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match) for this!

Now like all experience developers, I too have the lowest level of proficiency with regular expressions, so I'm going to use one I found online.

```javascript
let re = new RegExp(`
  \\bdelete\\s*\\([^\\)]*\\)|
  \\btypeof\\s*\\([^\\)]*\\)|
  \\bvoid\\s*\\([^\\)]*\\)
`);

// PS: this is the same regex being used in the Vue repo!

let matched = str.match(re);
let allowedPrefixes = ['.'];

if (matched && allowedPrefixes.includes(str.charAt(matched.index - 1))) {
  // continue and DO NOT throw a warning
}
```

---
### Step 3: Finding the right file

To simplify testing and debugging, I created a minimal setup - a `vue-oss-testing` folder which resides in the same directory as the cloned `vue` repository folder.

Within this folder, we need to setup an example similar to the codepen link in the issue and refer to the local copy of the the Vue dev build.

**Directory structure**
```
.
+-- vue 
+-- vue-oss-testing
|   +-- index.html
|   +-- app.js
```


**index.html**
```html
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="test">
    <div>
      <button @click="obj.delete()">Click here</button>
    </div>
  </div>
  <script src="../vue/dist/vue.js"></script>
  <script src="app.js"></script>
</body>
</html>
```

**app.js**

```javascript
let app = new Vue({
  data() {
    return {
      obj: {
        delete: function() {
          console.log('Calling delete function');
        }
      }
    }
  }
})

app.$mount('#test') // this is the entry point
```

The quickest way to find the desired file is to use this complicated series of steps called "Find All" 😁

Just go to VS Code (or whatever editor you prefer) and search for the error,

> avoid using JavaScript unary operator as property name

This led me to a file called `errorDetector.js` which had the following function which seems to be what we need to fix!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0ycdt9oz9snaa6ing9nw.png)

*Pro tip: You can run your `vue-oss-testing` project and set a break-point at this line in the dist vue.js file in DevTools and go through the call stack to understand the execution flow right from the first `app.$mount()` function.*

A quick glance at the function gives the notion that it allows for unary operators to be prefixed by `$` only, so we need to extend this solution to allow for more than just `$`.

We could fix making the following changes (similar to what we did earlier),

```javascript
const allowedPrefixes = ['.', '$']
if (keywordMatch && !allowedPrefixes.includes(stripped.charAt(keywordMatch.index - 1)))
```

Nothing else changes in the function,

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4gpcatp53pt3i8gctgj8.png)


You can now run your code again and observe the console - Vue doesn't throw a warning anymore!

---
### Step 4 - Write a test case

Now again, to find the right spec file, let's use our `Cmd/Ctrl + Shift + F` superpower! and we'll find this in the `template.spec.js` file,

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ipjyxot5lhu8qsbu61mb.png)

Let's add our spec after this,

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ea7nagfz9sf0xgksxdfn.png)

And that's it!

As the final step in the build/contribution step is to (raise a Pull Request)[https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request].

For this specific issue, there is already a [PR in review](https://github.com/vuejs/vue/pull/11143) with a very similar fix!

---
### Recap 

- Go to any open-source repo of your choice
- Filter through features or issues marked as "good first issue"
- Clone the code and replicate the issue
- Add a fix, refactor it to be robust!
- Raise a PR and wait for review comments

---
### Next Steps

Although I've covered a front-end repository here, I hope I was able to keep the steps generic enough for you to apply them to a language of your choice.

Here's a [list of all open issues](https://github.com/search?q=label%3A%22good+first+issue%22+label%3Ahacktoberfest+state%3Aopen&type=Issues&ref=advsearch&l=&l=) - labelled "good first issue" or "Hacktoberfest" - across all languages, so you can filter!

---
### What more? ... Participate!

Another way to be involved is to participate in the community channels, interact with developers - there are a lot of really good people out there and there's a lot to learn!

Don't be afraid to ask questions, there's no such thing as a stupid question.

Link to the VueJS discord channel - https://chat.vuejs.org/

---
And finally, I would love to hear your thoughts on this article. ✌
PS: Shout out to all the light-theme coders!