Setting up the development environment for js13k
Tags: javascript, webdev, tutorial
Published: 2020-08-24 11:19:59

This article is the second entry of a devlog on my participation to [js13k](https://js13kgames.com/) competition with [MogSogeking](https://github.com/MogSogeking)

<hr />

As we start developing our game, there are some tasks we want to avoid doing manually like:

- reloading our game each time we do some change;
- creating our submission file;
- checking if we reach the 13k limit;
- deploying our game to a public url (it is nice to gather feedback during development);

Before choosing a tool to help us achieve the tasks listed above it is important to know how our code must be structured. Here is the rule of js13k competition about the folder structure.  

> Your .zip package should contain index.html file in the top level folder structure

Our entry point must be an index.html file that loads our JavaScript files. [Parcel](https://parceljs.org) is a web application bundler that works with a html file as entry point. It requires almost no configuration. It seems to fit our needs so let's give it a try. 

Firstly, we install this new development dependency in our project.

## Development server

```sh
npm install --dev parcel-bundler
```

As explained in the [getting started](https://parceljs.org/getting_started.html) section of the documentation, the default command of the cli launchs a development server. It will rebuild our game each time we change a file and refresh the JavaScript code opened in the browser (without reloading the whole page!). We add a `dev` script to avoid typing this command each time we need it.

```json
  "scripts": {
    "dev": "parcel src/index.html"
  },
```

Let's create a few files to test it. Here is our folder structure:

```sh
.
├── package-lock.json
├── package.json
└── src
    ├── index.html
    └── index.js
```

Our `index.html` file import our JavaScript code with a `script` tag.

```html
<html>
  <body>
    <script src="./index.js"></script>
  </body>
</html>
```

To begin we add the well known **Hello world!**.

```js
console.log('Hello world!')
```

We start our dev server with the following command:

```sh
npm run dev
```

We should see a log `Hello world!` in our browser console. I change the message in our console.log parameter with my first name and save the file. 

```js
console.log('Hello Yvonnick!')
```

Our browser has reloaded our code automatically and display the updated log :tada:

## Submission file

Our development server is set up. We need to generate our submission file which is a zip file containing our index.html, JavaScript files and other assets.

### build

The js13k rules says we can optimize our JavaScript code. 

> You can use tools that minify JavaScript source code.

Parcel cli has a [build](https://parceljs.org/cli.html#build) command for this purpose. We add a few options to make it fill our needs.

```json
  "scripts": {
    "dev": "parcel src/index.html",
    "build": "parcel build src/index.html --no-source-maps --experimental-scope-hoisting --public-url ./",
  },
```

Let's get some explanations about these options.

#### --public-url option

Parcel prefixes the filename of the bundled javascript file with `public-url` in the `index.html` script tag. This option defaults to `/`. It is fine for a website stored on a static server.

> when unzipped should work in the browser.

If we try to open the built `index.html` we will see a blank page. Because the path of our JavaScript file is `/src.9905d997.js`. It will look for our JavaScript file at system root. Setting `public-url` to `./` will fix this issue since it will look for our JavaScript file in the current folder. Now, it works just fine when we open the built `index.html`.

#### --no-source-maps option

Source maps are files which reference a readable version of production that are most of the time minified. It helps us tracking bugs in our production bundle. Since our final zip size matters we don't want to include unnecessary files in it so we disable this feature. 

#### --experimental-scope-hoisting option

This option enables tree shaking during the build process. Tree shaking is a feature that prevents unused code of our dependencies being part of our production bundle. You can find out more in [this article](https://medium.com/@devongovett/parcel-v1-9-0-tree-shaking-2x-faster-watcher-and-more-87f2e1a70f79#4ed3) of Devon Govett.

### zip

At last, we add a `zip` script which creates a zip file with the content from `dist` folder that is the output of our `build` command.

```json
  "scripts": {
    "dev": "parcel src/index.html",
    "build": "parcel build src/index.html --no-source-maps --experimental-scope-hoisting --public-url ./",
    "zip": "zip -r submission.zip dist"
  },
```

Now, we can create our submission file using scripts :ok_hand:

## Adding a CI/CD

There are two last points to deal with:

- checking if we reach the 13k limit
- deploying our game to a public url (it is nice to gather feedback during development)

We don't want to figure out at the end of the competition that our submission file doesn't not fit the requirements. We don't want either to type our commands manually each time we commit some change. Since we need to host our source code on Github we will use [GitHub Actions](https://github.com/features/actions) to automate it.

> Please provide two sources of your game - first one should be minified and zipped to fit in the 13 kB limit (sent via the form) and the second one should be in a readable form with descriptive variable names and comments (hosted on GitHub).

I wont explain how GitHub Actions works but you can find a lot of nice articles on [dev.to](https://dev.to/search?q=GitHub%20Actions) on this topic.

### Checking our submission file size

First of all, we need to add a script that checks our submission file size. We will use [bundlesize](https://github.com/siddharthkp/bundlesize) to achieve it. It needs a bit of configuration (the file path to test, the max size it must not reach). We add it directly in our `package.json` file.

```json
"bundlesize": [
  {
    "path": "submission.zip",
    "maxSize": "13 kB"
  }
],
```

Then, we add a `size` script that calls `bundlesize`. It will throw an error if the zip file weight more than 13 kB.

```json
"scripts": {
  // ...
  "size": "bundlesize"
}
```

We create a first action in the file `.github/workflows/ci.yml` that will call our freshly created `size` script with the following content:

```yml
name: CI
on:
  pull_request:

jobs:
  size:
    runs-on: ubuntu-latest
    env:
      CI: true
  
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-node@v1
        with:
          node-version: 12
      - run: npm ci
      - run: npm run build
      - run: npm run zip
      - run: npm run size
```

We trigger this action only on pull request.

```yml
on:
  pull_request:
```

We create the zip file.

```yml
      - run: npm ci
      - run: npm run build
      - run: npm run zip
```

Finally, we run our `size` script which will prevent us to merge if it fails.

```yml
      - run: npm run size
```

Now, our CI checks for us our submission file size at each commit on pull request.

### Deploying our game

Cherry on the cake, we want to deploy our game on a public url so we can share it with friends to gather feedback during development. We create our second action in the file `.github/workflows/deploy.yml` with the following content:

```yml
name: Deploy
on:
  push:
    branches:
      - master
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
        - uses: actions/checkout@v1
        - run: npm ci
        - run: npm run build
        - uses: JamesIves/github-pages-deploy-action@3.5.9
          with:
            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
            BRANCH: gh-pages # The branch the action should deploy to.
            FOLDER: dist # The folder the action should deploy.
```
We trigger this action only on master branch.

```yml
on:
  push:
    branches:
      - master
```

We use the action [github-pages-deploy-action](https://github.com/JamesIves/github-pages-deploy-action) from [James Ives](https://github.com/JamesIves) which deploy our code on [GitHub Pages](https://pages.github.com/). Our app will be available at the url `https://[your-handle].github.io/[repository-name]` in my case [https://frinyvonnick.github.io/js13k-2020](https://frinyvonnick.github.io/js13k-2020).

We are all set 🙌 We can now develop our game at peace!

<hr/>

Follow me on [dev.to](https://dev.to/yvonnickfrin) or [twitter](https://twitter.com/YvonnickFrin) if you want to be informed when a new blogpost of this serie is published!