DEV Community

Cover image for Create your own CAPTCHA - part 2 - Setup TypeScript, Webpack and React
Meat Boy
Meat Boy

Posted on

Create your own CAPTCHA - part 2 - Setup TypeScript, Webpack and React

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 logo pilotpirxie / devcaptcha

🤖 Open source captcha made with React, Node and TypeScript for DEV.to community

DevCaptcha

devcaptcha

Open source captcha made with React, Node and TypeScript for DEV.to community

Blog series

Part 1 - Architecture




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.

{
  "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:

<!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.

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.

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 jsxspecifies 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.

{
  "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.

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:

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
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 logo pilotpirxie / devcaptcha

🤖 Open source captcha made with React, Node and TypeScript for DEV.to community

DevCaptcha

devcaptcha

Open source captcha made with React, Node and TypeScript for DEV.to community

Blog series

Part 1 - Architecture

If you want to be notified about the next part, follow me on DEV.to. 😉

meatboy image

Discussion (0)