Friday, October 22, 2021

Visual Studio Code + TypeScript + React - how to start in 10 minutes (fall 2021 tutorial)

Just a short tutorial on how to start your adventure with TypeScript + React with Visual Studio Code. Without further ado:
Install node.js. Install Visual Studio Code.
Create an empty folder. Go to it and run VSC from there from the OS shell
code .
When in VSC, open the terminal and work from the terminal rather than the OS shell
npm init -y
npm install webpack webpack-cli
npm install typescript ts-loader
npm install react react-dom
npm install @types/react @types/react-dom
This will create package.json and install your dependencies.
At the root of your app create two files, tsconfig.json

{
    "compilerOptions": {
        "allowJs": false,
        "baseUrl": "./",
        "jsx": "react",
        "declaration": false,
        "esModuleInterop": true,
        "lib": ["ES6", "DOM"],
        "module": "commonjs",
        "moduleResolution": "node",
        "noImplicitAny": true,
        "outDir": "./dist/",
        "paths": {
        "@/*": ["src/*"]
        },
        "sourceMap": true,
        "target": "ES6"
    }
}
and webpack.config.js
    const path = require('path');
    const TerserPlugin = require("terser-webpack-plugin");

    module.exports = {
    //mode: 'production',
    mode: 'development',
    target: ['web','es6'],
    entry: {
        'index': './src/index.tsx'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    devtool: 'source-map',
    module: {
        rules: [{
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
        }, ],
    },
    externals: {
        // Use external version of React
        //'react': 'React',
        //'react-dom': 'ReactDOM'
    },
    resolve: {
        // https://getfrontend.tips/shorten-import-paths-in-webpack/
        alias: {
        // Assume that the `src` folder is located at the root folder
        '@': path.join(__dirname, 'src'),
        },
        extensions: ['.tsx', '.ts', '.js'],
    },
    optimization: {
        minimize: false, // true/false
        minimizer: [
        new TerserPlugin({
            extractComments: false,
            terserOptions: {
            format: {
                comments: false,
            },
            },
        })
        ],
    },
    };
There are few options in the webpack's configuration file that can be further changed, pay close attention to mode and minimize.
Now create two empty folders, src and dist, the first is where your source code goes, the second is where webpack will store its output.
Now create two TypeScript files. First is your entry point, index.tsx
import React from 'react';
import ReactDOM from 'react-dom';

import App from '@/app';

/**
 * Entry point
 */
class Program {
    
    Main() {

        var app = (
                <App />
        );

        ReactDOM.render(app, document.getElementById('root'));
    }
}

new Program().Main();
or using the new API that replaces ReactDOM.render
import React from 'react';
import { createRoot } from 'react-dom/client';

import App from '@/app';

/**
 * Entry point
 */
class Program {
    
    Main() {

        const app = (
            <React.StrictMode>
                <App />
            </React.StrictMode>
        );            

        const root = createRoot(document.getElementById('root'));

        root.render( app );    
    }
}

new Program().Main();
Second is your app's component, app.tsx
import React from 'react';

const App = () => {

  return <>  
    Hello world from React & Typescript
  </>
};

export default App;
The general rule here is that
  • if a module is a pure TypeScript, your file can have *.ts extension
  • if a module should contain JSX code, your file should have *.tsx extension
Modules are imported/exported between both without any restrictions.
Now add the last file, app.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script defer src="/dist/index.js"></script>
    <style>
    * , *:before, *:after { 
         box-sizing:border-box; 
    }
    html {
        background-color: rgb(234, 238, 243);
    }

    </style>
</head>
<body>    
    <div id="root"></div>
</body>
</html>
What you should have right now is
Now just invoke
webpack
(this assumes webpack is available globally, if not, npm-install both webpack and webpack-cli as global modules:
npm install -g webpack webpack-cli
) and the application will be built and the output, dist/index.js will be created. This is a single file bundle and it can be further optimized by switching mode to production in the config, as well as turning on minimization.
To test the app, install and run a simple HTTP server, e.g. live-server
npm install -g live-server
live-server
and add a launch configuration in VSC
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "pwa-chrome",
            "request": "launch",
            "name": "Launch Chrome against localhost",
            "url": "http://localhost:8080/app.html",
            "webRoot": "${workspaceFolder}"
        }
    ]
}
Congratulations, you can start your app with F5 from within VSC, put breakpoints in your TypeScript and debug your code and continue work on your first great app. Happy coding!

Wednesday, October 20, 2021

IndexedDB can possibly fail on FireFox

A couple of minutes and a good coffee spent on an issue. The idb, a wrapper over IndexedDB was failing silently. An isolated demo was luckily showing something, it was A mutation operation was attempted on a database that did not allow mutations in the console.
What Google tells about this issue is that it's related to working in private mode. Which was definitely not my case. However, another very old discussion mentions a setting that turns off history. And yes, the setting is there, in Firefox. Go to Options, then Security and Privacy and then there's a switch that either turns on or turns off history tracking. For some reason it was turned off in my FF.
And yes, turning it on brings IndexedDB back to life.

Friday, October 1, 2021

tiny-totp.js, a tiny TOTP javascript implementation

Just published the tiny-totp.js code on Github. It's super tiny Javascript RFC6328 implementation. If you wonder what's that, it's the Timed One-Time Passwords specs that is widely used to implement two-factor authentication over web.

What this tiny library does is it computes one time passwords, given the master password. It's basically the same computation Google Authenticator or Microsoft Authenticator do, given the master password. It means that you can either implement your own client or even your own server side authentication that would be compatible with existing authenticators (including the two).

What's interesting here is that the implementation is really, really tiny and has 0 dependencies. Usually, people implement TOTP using two dependencies:

  • to handle the base32 encoding (master keys are usually provided in base32 encoding
  • to handle the hmacsha1 to calculate actual codes
This code avoids both dependencies - there's a tiny function to handle base32 and platform specific crypto provider is used to handle hmacsha1 (the node's crypto module when run against node or window.subtlecrypto when run in a browser).