Creating a template repository in GitHub
Tags: github, tutorial, beginners, javascript
Published: 2020-06-01 13:18:50

I decided to start my new project with the development of a template for GitHub repositories, and in this article, I want to share my experience!

By the way, the whole process that will be discussed later I recorded on video (with all my errors along the way 😄) take a look, suddenly you will like it 😉

{% youtube EjeY-7EtDiM %}

## Why do I need a template for the GitHub repository?

A template is a very convenient tool if you often start developing new projects and you need a preconfigured starting template with installed dependencies, structure and customized automation processes

## Step by step

Create a new GitHub repository. At this stage, this repository is no different from your regular repositories

![The example of my new repository](https://dev-to-uploads.s3.amazonaws.com/i/bofntpjnf828bnrdelh6.png)
<figcaption>The example of my new repository</figcaption>

Go to your local folder where your project will be located. Create a new git repository (you can immediately execute the commands that GitHub offers after creating the remote repository) and create the `package.json` file

```
git init
npm init
```

npm will ask you some questions and based on your answers will create a basic `package.json`, but since this file is very important for us, let's take a closer look

### name

This is the identifier of your package, which must be unique. Advice from me, check in advance if the name is free at [npmjs.com](https://www.npmjs.com/) if you are going to publish your package there

### version

Shows the current version of the application

### description

A brief description of your project

### main

The main entry point to your project. This field (and the next two) should indicate the place where your package will be collected (I usually use the `dist` folder)

### modules

Pointer to an ECMAScript Module

### types

Type в declaration pointer for TS

### files

The list of files that will be included in the build after your package is installed as a dependency. I recommend placing only the files necessary for your package to work, it makes no sense to install all the files that you use during the development process (here I just specify the `dist` folder)

### repository

It's important to specify the place where your code is stored for the convenience of contributors (just copy the link to your GitHub repository here)

### author

Just indicate yourself or your team

### license

Indicate how other users can use your package. This information also appears in your package when published to npm and to GitHub. GitHub also recommends adding the `LICENSE.md` file to expand the license. In my case, I choose MIT

### keywords

List of keywords by which other people can find your package

### bugs

Link to where users report problems in your project. In my case, this is a link to GitHub issues

As a result, I got such a `package.json` file:

```json
{
  "name": "como-north",
  "version": "1.0.0",
  "description": "GitHub template for starting new projects",
  "main": "./dist/index.js",
  "module": "./dist/index.es.js",
  "types": "./dist/index.d.ts",
  "files": [
    "dist"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/Alexandrshy/como-north"
  },
  "author": "Alex Shualev <alexandrshy@gmail.com>",
  "license": "MIT",
  "keywords": [
    "template",
    "javascript",
    "package"
  ],
  "bugs": {
    "url": "https://github.com/Alexandrshy/como-north/issues"
  },
  "homepage": "https://github.com/Alexandrshy/como-north",
}
```

Do not forget to make commits, if you have already watched [my video](https://youtu.be/EjeY-7EtDiM), I do it constantly 😄

Now let's move on to the tools. I will not dwell on individual libraries for a long time or explain my choice, each tool is variable and can be replaced or completely removed, I just tell you one of the options that suit me

### Linters

In my template, I'll use a bunch of ESLint and Prettier. In my opinion, this is the best choice at the moment because of the flexibility of the settings

#### 📦 Prettier

Prettier is a code formatting tool that aims to use predefined rules for the code design. It formats the code automatically and has extensions for modern IDE

Install the package:

```
npm i prettier -D
```

Write the configuration:

```
{
  "singleQuote": true,
  "parser": "typescript",
  "tabWidth": 4,
  "bracketSpacing": false,
  "printWidth": 100,
  "trailingComma": "all"
}
```

You can also create `.prettierignore` if you have files that you do not want to format

```
.github/
.idea/
node_modules/
dist/
```

#### 📦 ESLint

This tool analyzes the code to help detect problematic patterns that don't comply with the rules and standards. It works for most programming languages and has a large number of ready-made configurations from large companies and extensions for various tasks

Install the package:

```
npm i eslint eslint-config-prettier eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-jsdoc -D
```

* **eslint-config-prettier** - is a config that disables rules that conflict with Prettier
* **eslint-plugin-prettier** - exposes a "recommended" configuration that configures both `eslint-plugin-prettier` and `eslint-config-prettier` in a single step
* **@typescript-eslint/eslint-plugin** - an ESLint-specific plugin which, when used in conjunction with `@typescript-eslint/parser`, allows for TypeScript-specific linting rules to run
* **@typescript-eslint/parser** - an ESLint-specific parser which leverages `typescript-estree` and is designed to be used as a replacement for ESLint's default parser, espree
* **eslint-plugin-jsdoc** - JSDoc linting rules for ESLint

All additional packages are optional and depend on your goals. You can also pay attention to **eslint-config-airbnb** this package provides the developer with the configuration from Airbnb

```js
module.exports = {
    plugins: ['@typescript-eslint', 'prettier', 'jsdoc'],
    extends: [
        'plugin:@typescript-eslint/recommended',
        'prettier/@typescript-eslint',
        'plugin:prettier/recommended',
        'plugin:jsdoc/recommended',
    ],
    rules: {},
    overrides: [
        {
            files: ['src/*/*'],
            rules: {
                'max-lines': 'off',
                'max-nested-callbacks': 'off',
                'max-statements': 'off',
            },
        },
    ],
    settings: {
        node: {
            extensions: ['.ts', '.json'],
        },
    },
};
```

Add scripts for linter:

```
"prettier": "prettier '**/*.{js,ts}' --ignore-path ./.prettierignore",
"lint": "eslint '*/**/*.{js,ts}'",
```

### 📦 TypeScript

JavaScript is a dynamically typed language, meaning the compiler doesn't know what type of variable you are using until the variable is initialized. Such things can cause difficulties and errors in your projects. However, TypeScript helps solve these problems. I use TypeScript in all my projects regardless of their size. I believe that early catching errors is very important and it is better to foresee the structure of your functions in advance than spending time catching bugs later

```
npm i typescript -D
```

The `tsconfig.json` file specifies the root files and the compiler options required to compile the project.

```json
{
    "compilerOptions": {
        "outDir": "dist",
        "module": "es2015",
        "target": "es6",
        "lib": [
            "es5",
            "es6",
            "es7",
            "es2017",
            "dom"
        ],
        "sourceMap": true,
        "moduleResolution": "node",
        "baseUrl": "src",
        "skipLibCheck": true,
        "strict": true,
        "declaration": true
    },
    "include": [
        "src",
        "typeScript"
    ],
    "exclude": [
        "node_modules",
    ]
}
```

You can find all available options [here](https://www.typescriptlang.org/docs/handbook/compiler-options.html)

Add some more scripts:

```
"types": "tsc --noEmit",
"finish": "npm run lint && npm run types"
```

The `finish` script we need when working on `workflows`

Now we can create the `src/index.ts`

```js
export const union = (a: Array<string>, b: Array<string>): Array<string> => [...a, ...b];
```

And now we can run `finish` script

```
npm run finish
```

If everything is done correctly we will not get any error

### 📦 Babel

We'll add Babel to the template for the correct operation of our code in older versions of browsers

```
npm i @babel/core @babel/preset-env @babel/preset-typescript -D
```

Add configuration file

```js
module.exports = {
    presets: [
        ['@babel/preset-env', {targets: {node: 'current'}, modules: false, loose: true}],
        '@babel/preset-typescript',
    ],
};
```

Need to pay attention to

#### targets

Describes the environments you support/target for your project. You need to specify a minimum environment required for your users

#### modules

Enable transformation of ES6 module syntax to another module type

#### loose

Enable ["loose" transformations](https://2ality.com/2015/12/babel6-loose-mode.html) for any plugins in this preset that allow them

We will not create a separate script for running babel since we'll use babel through the plugin in rollup

### 📦 Rollup.js

Rollup is a module bundler for JavaScript. Now the Rollup community is very active, and I often see new projects that use Rollup for building. Its main advantage is its easy configuration. Let's add Rollup to the project and write a config file

```
npm i rollup rollup-plugin-terser rollup-plugin-typescript2 @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve -D
```

As you can see, in addition to the main package, we install many extensions, let's say a few words about each:

* **rollup-plugin-terser** - plugin to minimize the generated package
* **rollup-plugin-typescript2** - plugin for typescript with compiler errors
* **@rollup/plugin-babel** - plugin for seamless integration between Rollup and Babel
* **@rollup/plugin-commonjs** - plugin to convert CommonJS modules to ES6, so they can be included in a Rollup bundle
* **@rollup/plugin-node-resolve** - plugin which locates modules using the [Node resolution algorithm](https://nodejs.org/api/modules.html#modules_all_together), for using third party modules in `node_modules`

And now the config file itself

```js
import typescript from 'rollup-plugin-typescript2';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import {terser} from 'rollup-plugin-terser';

import pkg from './package.json';

const extensions = ['.js', '.jsx', '.ts', '.tsx'];

export default {
    input: 'src/index.ts',
    output: [
        {
            file: pkg.main,
            format: 'umd',
            name: 'ComoNorth',
        },
        {
            file: pkg.module,
            format: 'es',
        },
    ],
    plugins: [
        typescript({
            rollupCommonJSResolveHack: true,
            clean: true,
        }),
        babel({
            exclude: 'node_modules/**',
            extensions,
        }),
        resolve(),
        commonjs(),
        terser(),
    ],
};
```

Add new scripts:

```
"build": "npm run build:clean && npm run build:lib",
"build:clean": "rimraf dist",
"build:lib": "rollup -c",
```

To understand that we did everything right, let's run the script. As a result, we should not see any errors in the console and a new dist folder should appear in the project

```js
npm run build
```

### 🔥 Automation

In your project, you should think not only about the dev build, but also the delivery processes of your package to your users. Each of your changes should be reflected in a file with changes so that other people can follow the development process, your project must be correctly versioned according to your changes and published immediately (in my case in npm). Let's take it in order

#### Checking the commit message

Since we want to record all changes made to our package, we need to structure our commit messages. For this, we'll use [commitlint](https://github.com/conventional-changelog/commitlint)

```
npm i @commitlint/cli @commitlint/config-conventional husky -D
```

Configuration file

```json
{
    "parserPreset": "conventional-changelog-conventionalcommits",
    "rules": {
        "body-leading-blank": [
            1,
            "always"
        ],
        "footer-leading-blank": [
            1,
            "always"
        ],
        "header-max-length": [
            2,
            "always",
            150
        ],
        "scope-case": [
            2,
            "always",
            "lower-case"
        ],
        "subject-case": [
            2,
            "never",
            [
                "sentence-case",
                "start-case",
                "pascal-case",
                "upper-case"
            ]
        ],
        "subject-empty": [
            2,
            "never"
        ],
        "subject-full-stop": [
            2,
            "never",
            "."
        ],
        "type-case": [
            2,
            "always",
            "lower-case"
        ],
        "type-empty": [
            2,
            "never"
        ],
        "type-enum": [
            2,
            "always",
            [
                "chore",
                "ci",
                "docs",
                "feat",
                "fix",
                "refactor",
                "revert",
                "style",
                "test"
            ]
        ]
    }
}
```

As you may have noticed we also installed the [husky](https://github.com/typicode/husky#readme) package as a dependency. This package is very well described on their page on GitHub: "Husky can prevent bad `git commit`, `git push` and more". Required for correct operation `husky`: `node` >= 10 and `git` >= 2.13.0

Add the following code to `package.json`:

```json
"husky": {
  "hooks": {
    "pre-commit": "lint-staged",
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
},
"lint-staged": {
  "src/**/*.{js,ts}": [
    "npm run lint"
  ]
},
```

Now, before each attempt to make a `git commit`, we will run the `lint` script, and each commit message we will check for compliance with the template. Experiment time, try the following code:

```
git add .
git commit -m "added commitlint and husky"
```

And we get an error, but that is what we were waiting for! This means that we cannot make commits with arbitrary commits

![Errors](https://dev-to-uploads.s3.amazonaws.com/i/fidtb2890tm958fiw31k.png)

```
git add .
git commit -m "feat: added commitlint and husky"
```

But this will work. The `feat` tag that we used in the commit message is necessary for further versioning our package

#### GitHub workflows

You can create custom workflows to automate your project's software development life cycle processes. [Detailed workflow instructions](https://help.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow).

The first process that we will set up is the process of working with pull requests. Typically, this process involves building the project, checking linter, running tests, and so on

First, create a file `.github/workflows/pull-requests_check.yml`

And add the following

```yml
name: Pull-Requests Check

on: [pull_request]

jobs:
  Test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v1
      with:
        node-version: 12

    - name: Finish
      env:
        GH_TOKEN: ${{ secrets.GH_TOKEN }}
      run: |
        npm i
        npm run finish
```

After these files get into your GitHub repository and you create a new pull request in the `Actions` tab, you'll see a window with the result of your process. If everything is green, excellent, you can merge your request!

![Pull-Requests Check result](https://dev-to-uploads.s3.amazonaws.com/i/fl5s9ok5x7zjvroht3qh.png)

It took me 20 seconds to complete my process, but it all depends on the complexity of your workflow, if you run a large number of tests for your project, it may take several minutes

Now let's create a more complex workflow for automatically publishing the package in npm and recording changes to the new version of the package in `CHANGELOG.md`

```yml
name: Release

on:
  push:
    branches:
      - master

jobs:
  Release:
    name: release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          always-auth: true
          node-version: 12
          registry-url: "https://npm.pkg.github.com"
          scope: "@Alexandrshy"

      - name: Install dependencies
        run: npm i

      - name: Build
        run: npm run build

      - name: Semantic Release
        uses: cycjimmy/semantic-release-action@v2
        id: semantic
        with:
          branch: master
          extra_plugins: |
            @semantic-release/git
            @semantic-release/changelog
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Push updates to branch for major version
        if: steps.semantic.outputs.new_release_published == 'true'
        run: git push https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git HEAD:refs/heads/v${{steps.semantic.outputs.new_release_major_version}}
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
```

Here you should pay attention to the following two things

1. We used the [GitHub Actions](https://github.com/features/actions) [cycjimmy/semantic-release-action@v2](https://github.com/cycjimmy/semantic-release-action) which in turn is a wrapper over [semantic-release](https://github.com/semantic-release/semantic-release). GitHub Actions has a lot of useful tools for automating various processes, just check out the market place and you will be surprised 🙂
2. `secrets.GH_TOKEN` and `secrets.NPM_TOKEN` GitHub provides a token that you can use to authenticate on behalf of GitHub Actions. These tokens must be generated (for [npm](https://www.npmjs.com/settings/alexandrshy/tokens) and for [GitHub](https://github.com/settings/tokens)) and added to your repository (for example https://github.com/{your-name}/{repository-name}/settings/secrets)

If you didn't make any mistakes, you'll get your package published in npm

![Result actions](https://dev-to-uploads.s3.amazonaws.com/i/mb2ajk41eo6qgdtp2tsp.png)
<figcaption>Completed action after merga in the master</figcaption>

![CHANGELOG.md](https://dev-to-uploads.s3.amazonaws.com/i/e2mw9r9hsf5vf5u3bscf.png)
<figcaption>Automatically filled in CHANGELOG.md based on commit messages</figcaption>

![Package in npm](https://dev-to-uploads.s3.amazonaws.com/i/wk82u3p6rixj3mtikw0h.png)
<figcaption>Package was published on npm and will publish new versions after each merge</figcaption>

Now every change that gets into the master branch will start this process and create a new version depending on the tags that you added to your commit message. For example, if you had version 1.0.0 of the package and you made a merge with the commit message: "fix: eslint config" after the workflow is complete, you'll receive a new version of package 1.0.1

### Dependency management

To control dependencies, I recommend that you add dependentabot. This bot automatically checks your dependencies and the need to update them

[On the site](https://app.dependabot.com/) you need to sign in via GitHub. Then give access to those repositories that dependabot should monitor

And in the project itself you need to create a `.dependabot/config.yml` with this content:

```yml

version: 1
update_configs:
  - package_manager: "javascript"
    directory: "/"
    update_schedule: "weekly"
    target_branch: "master"
    commit_message:
      prefix: "fix"
    target_branch: "dependa"
    default_reviewers:
      - Alexandrshy
```

![Example of running validation](https://dev-to-uploads.s3.amazonaws.com/i/u18vrlqpjwch3i0kwnv7.png)
<figcaption>Example of running validation</figcaption>

You can configure the auto-merge immediately in the master, but I would not recommend doing this, I decided to put all the updates in a separate branch and then update the master yourself using a single pull request

![Dependentabot pull request example](https://dev-to-uploads.s3.amazonaws.com/i/u18vrlqpjwch3i0kwnv7.png)
<figcaption>Dependentabot pull request example</figcaption>

### Minor improvements

All we have to do is add `README.md` and `LICENSE`

`README.md` is your space for creativity, but don't forget, its main purpose is to show you how to work with your package very briefly. You can create a `LICENSE` via the GitHub interface. This is convenient, since GitHub has pre-prepared templates. 

![Creating a LICENSE File](https://dev-to-uploads.s3.amazonaws.com/i/ymgnd2oxd79ebfcnsd3x.png)
<figcaption>Creating a LICENSE File</figcaption>

To do this, click on the button "Create new file". Enter the file name `LICENSE`, then click on the button "Choose a license template" and select the license that suits you

![Made the project a template for GitHub](https://dev-to-uploads.s3.amazonaws.com/i/09zqysnv9q5nrkmqhfqu.png)
<figcaption>Made the project a template for GitHub</figcaption>

Well and most importantly, make the resulting project a template for your future work. To do this, we go to the repository settings and click on the "Template repository" checkbox, that's all!

## Work result

[Como-north](https://github.com/Alexandrshy/como-north) my template that I'll use in my next projects and maybe I'll update it as needed

## Video

{% youtube EjeY-7EtDiM %}

## Links

* [Babel](https://babeljs.io/)
* [Rollup.js](https://rollupjs.org/)
* [TypeScript](https://www.typescriptlang.org/)
* [ESlint](https://eslint.org/)
* [Prettier](https://prettier.io/)
* [Commitlint](https://github.com/conventional-changelog/commitlint)
* [Husky](https://github.com/typicode/husky)
* [Semantic release](https://github.com/semantic-release/semantic-release)
* [Semantic release action](https://github.com/cycjimmy/semantic-release-action)
* [About package.json](https://docs.npmjs.com/files/package.json)