Create your own CAPTCHA - part 2 - Setup TypeScript, Webpack and React
Tags: react, typescript, javascript, tutorial
Published: 2020-05-18 12:15:55

Welcome in the second part of the series about creating own, custom captcha mechanism. In this article, we are going to prepare an environment for further work. As I mentioned in the previous post, the entire captcha will be written with TypeScript and React on the client-side.

If you want to skip reading, just download source code from git repository. Leave a star if you like the project. ⭐
{% github pilotpirxie/devcaptcha %} 

### Installation
So let's initialize the project with installing libraries. Install react, react-dom, styled-components, webpack and typescript. Then install types, eslint and utils plugins. 

To install libs faster, just copy them from package.json below that I prepared. Then run ``yarn`` and ``yarn upgrade --latest`` to upgrade to the newest version.

```bash
{
  "name": "devcaptcha",
  "version": "1.0.0",
  "main": "dist/devcaptcha.dist.js",
  "devDependencies": {
    "@types/react": "^16.9.35",
    "@types/react-dom": "^16.9.8",
    "@types/styled-components": "^5.1.0",
    "@typescript-eslint/eslint-plugin": "^2.33.0",
    "@typescript-eslint/parser": "^2.33.0",
    "eslint": "^7.0.0",
    "eslint-plugin-react": "^7.20.0",
    "source-map-loader": "^0.2.4",
    "ts-loader": "^7.0.4",
    "typescript": "^3.9.2",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.11.0"
  },
  "dependencies": {
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "styled-components": "^5.1.0"
  },
  "scripts": {
    "start": "webpack-dev-server --open --config webpack.development.config.js",
    "build": "webpack --config webpack.production.config.js",
    "eslint": "./node_modules/.bin/eslint .",
    "fix": "./node_modules/.bin/eslint --fix ."
  }
}
```

### Dev server for hot reload
After installation, create directory ``public`` and ``index.html`` file and put inside:
```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
</head>
<body>
<div id="captcha"></div>

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="main.js"></script>
</body>
</html>
```

This file will be served on the dev server. Before closing body tag it contains links to React on CDN, just for the development process. We want to have an independent file similar to Web Components (if you want, you can wrap this project and create custom element) to work in different situations.

### Webpack
Create ``webpack.development.config.js`` file for development like below. Configure port and public directory. This file also contains information about bindings in source maps between original and minified files. Make sure to install ``ts-loader`` to be able to resolve and load typescript files. 
```javascript
module.exports = {
  mode: "development",
  devtool: "source-map",
  devServer: {
    contentBase: './public',
    compress: false,
    port: 8080,
  },
  resolve: {
    extensions: [".ts", ".tsx", '.js', '.json']
  },
  module: {
    rules: [{
      test: /\.ts(x?)$/,
      exclude: /node_modules/,
      use: [{
        loader: "ts-loader"
      }]
    }, {
      enforce: "pre",
      test: /\.js$/,
      loader: "source-map-loader"
    }]
  },
  externals: {
    react: "React",
    "react-dom": "ReactDOM",
  }
};
```

Similarly, create production config for builds ``webpack.production.config.js``. It's very close to the previous, however it doesn't contain dev server configuration, different mode and externals. Externals are used to skip and create globals. In the dev mode, we are using CDN links to make hot reload faster. In the prod we want to bundle everything together.
```javascript
module.exports = {
  mode: "production",
  devtool: "source-map",
  output: {
    filename: 'devcaptcha.dist.js'
  },
  resolve: {
    extensions: [".ts", ".tsx", '.js', '.json']
  },
  module: {
    rules: [{
      test: /\.ts(x?)$/,
      exclude: /node_modules/,
      use: [{
        loader: "ts-loader"
      }]
    }, {
      enforce: "pre",
      test: /\.js$/,
      loader: "source-map-loader"
    }]
  },
};
```

### Typescript
Create configuration for typescript ``tsconfig.json``. Parameter ``noImplicitAny`` set to true disallow compilation when somewhere variable is untyped. Parameter ``jsx``specifies that we are using ``tsx`` files. Library array contains different types of load by default. Entry ``dom`` allow accessing to Web API and objects like ``window.document``.
```json
{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "CommonJS",
    "jsx": "react",
    "target": "es5",
    "lib": [
      "es6",
      "dom"
    ]
  }
}
```

### Initial source code
Ok, almost ready. You need to create an entry point for typescript. With React we will be using ``.tsx`` extension. It's like typescript with some additional sugar. 

Create directory ``src`` and ``index.tsx`` inside. Inside import entire React and ReactDOM and create a class with a method for rendering/mounting captcha in the right place.

In my case, I am looking for root element by selector passed in the constructor. The class that I created implements interface ICaptcha with common properties for hypothetical, different captchas and DevCaptcha too.

Important is to assign the reference to DevCaptcha on window object to make access possible. However, in TypeScript, you cannot assign directly to a global object. Firstly declare an extended interface to the object.

```typescript
import * as React from "react";
import * as ReactDOM from "react-dom";

import { App } from "./components/App";

interface ICaptcha {
  _appendSelector: string
}

type CaptchaConfig = {
  appendSelector: string
}

class DevCaptcha implements ICaptcha {
  readonly _appendSelector : string;

  public constructor(config : CaptchaConfig) {
    this._appendSelector = config.appendSelector;
  }

  mount() {
    ReactDOM.render(<App />, document.querySelector(this._appendSelector));
  }
}

declare global {
  interface Window { DevCaptcha: object; }
}

window.DevCaptcha = window.DevCaptcha || {};
window['DevCaptcha'] = DevCaptcha;
```

### ESLint
Finally, configure eslint to quickly look for the code quality problems. You can configure it for you. If you have your own eslint config, just use it. 

Create ``.eslintrc`` with the following code:

```javascript
module.exports = {
  "env": {
    "browser": true,
    "commonjs": true,
    "es6": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/eslint-recommended"
  ],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true,
    },
    "ecmaVersion": 2018
  },
  "plugins": [
    "react",
    "@typescript-eslint"
  ],
  "rules": {
    "indent": ["error", 2]
  }
};
```

and ``.eslintignore`` with directories to exclude

```
node_modules
public
dist
```

### You did it!
If you did everything well, you should be able to run dev server of this app.
```
yarn start
```
![You did it](https://media.giphy.com/media/QaXcpBEQRfD9pR3zk5/source.gif)
Open browser on localhost at the port which you set up previously. In my case, it's 8080, so open ``http://localhost:8080``. You should see Hello World setup for React, widget-based application.

Uff. That's how we prepared environment for future work on client-side of captcha. In the next article, we will start working on a first reverse-turing mechanism.

Current source code is available on GitHub. Please, leave a star ⭐ if you like project.
{% github pilotpirxie/devcaptcha %} 

If you want to be notified about the next part, follow me on DEV.to. 😉
{% user meatboy %}