Introducing Lesky: The Multi-lingual CLI for Rapidly Starting KoaJS-based Applications
Tags: javascript, koajs, cli, lightweight
Published: 2019-12-17 22:51:24

TL;DR - [Lesky](https://www.npmjs.com/package/lesky) is meant to be a relatively lightweight multi-lingual CLI that gets installed once (globally) and used anywhere, invoked with `les` (not more, because `les` is less). Specifically, any folder can be statically served and watched for changes. Also, any folder can be initialized as a KoaJS-based app rapidly using the CLI. It's as if `http-server` and `express-generator` gave birth to a ES6-based mutli-lingual baby. This baby knows 42 different languages.

Disclaimer: I am the author of lesky.

---

## Introduction:

Many problems that lesky intends to solve are solved pretty well by other great,  but separate, projects. However, the goal with lesky was to bring a lot of great ideas together in one package to solve the following problems, while requiring less effort from the user:

* Run it anywhere on the machine to serve static content. Sometimes I require or prefer fiddling locally on machine rather than online, since it's usually much faster.
* Support all http protocols (http, https, http2), not just http (old as 1995!). http3 is on the radar. 
* Consume a configuration file of server configs (so I don't have to type it in again)
* Immediately open the default browser and watch for file changes
* Disable cache-control by default, since it's a dev server and I want to see the changes as I make them.
* Separate the concerns of the server from the application, database, IO, and CLI. This way, if the server has to be changed out with a different version of Koa, or a different server framework, it should be easy enough to do so.
* Initialize any workspace with KoaJS boilerplate, while still separating the concerns of the server and app. Ideally, the workspace would be initialized with eslint, babel, test framework and other configuration files so that it would be ready to go. Those extras add a bit of weight to the project, but I think it's worth it. (I wanted something like express-generator, but less typing, and less code to refactor based on my personal preferences)
* It had to be reusable and have reusable utils. More of those utils can be found in [`les-utils`](https://www.npmjs.com/package/les-utils)
* It had to be mutli-lingual because I think people will think the best when they are able to use it in their native language. This was the most challenging task, but worth doing, I think.

## Installing:

It is recommended to install lesky globally so you don't have to do it again:

> npm i -g lesky

If you do not currently have your npm global path searchable, you simply need to update your PATH environment variable: 

> export PATH=~/.npm-global/bin:$PATH

Important note: even though the project is called "lesky", the command will be "les". "les" was desired for the project name, but taken.

## Basic Usage:

> les [path] [options]

So, for example, simply typing:

> les

this will serve static content that is in the "public" folder (If you run the command from /myproject, then /myproject/public needs to exist; if it doesn't simply create it or specify the path you want to serve)

To quickly see the application, it's simply a matter of: 

> les -ow # "less ouch", get it?

The 'o' opens the browser, and the 'w' watches for file changes. It may help to include my [dumb reloader script](https://raw.githubusercontent.com/richardeschloss/les/master/public/webpack/dumbReloader.slim.js) in the html file you wish to auto reload on changes. This is nowhere near as cool as hot module reloading, but it's good enough for quick-and-dirty development work.

## Configuring Lesky

Making lesky easy to configure was one of the key goals, and built on a CLI-design pattern that seems to be common today. The pattern is: allow CLI arguments to come first from a configuration *file* and then if arguments are *also passed in* on the command line, give priority to those arguments. This pattern makes it real easy to "code with config" and at the same time, overwrite the config on the command line when needed, without actually changing the config file.

The CLI options can always be found in the help menu:

> les -h

```
usage: les [path] [options]

options:
    -h,	--help	Print this help menu
    -i,	--init	Init lesky in workspace specified by path, defaults to cwd [[cwd]]
    -a,	--host	Address to use [localhost]
    -p,	--port	Port to use [8080]
        --proto	Protocol to use [http] ({ http, https, http2 })
        --range	Port Range (in case port is taken) [8000,9000]
        --sslKey	Path to SSL Key
        --sslCert	Path to SSL Certificate
    -o,	--open	Open browser [OS default]
    -w,	--watch	Watch for file changes in dir [staticDir]

---End of Help---
```

This means that any of the long-form options shown here can be entered in the terminal *or* in the config file:

> les --port 8001

Is equivalent to having a les config file (`.lesrc`) with the entry:

```
[{ "port": 8001 }]
```

Then, simply typing `les` will start the server at the configured port 8001. There are important things to note with the config file and the CLI. In the config file, the structure is an array of server configs. This way, if you want to host the "http" version at port 8001, and the "https" version at port 8002, this config file is all that you need! No extra changes to code:

```
[{
  "host": "localhost",
  "proto": "https",
  "port": 8002,
  "sslKey": ".ssl/server.key",
  "sslCert": ".ssl/server.crt"
},{
  "proto": "http",
  "port": 8001
}]
``` 

Important notes and limitations:
1. The configuration file is the only way to specify multiple server configs. I.e., `les --proto http --proto https` may not do what you think it will.
2. The configuration file requires the long-form option names, not the aliases. The aliases are only respected in the terminal. The reason is, I wanted this file to support multiple languages. The aliases are fixed, but the long-form option names are changed based on user locale. I think this design choice may result in a better developer experience. 

## Initializing a workspace

More than likely, it will be desired to create a custom lesky-based / KoaJS-based application in the folder you are currently working in. You'll want to do more than serve static content. This is pretty simple to do with `les`:

> les [path] --init [options]

Where path specifies the directory to initialize (defaults to the current working directory). Any options that get passed in at this step will be used to auto-generate the `.lesrc` config file so you'll never have to enter it again. Just consume it.

## Multi-lingual support

The tool currently supports 42 different languages. In order for it to work correctly, it expects the "LANG" environment variable to be set and to include the language's 2-character code. For example, on my system, it's "en_US.UTF-8" (char code == "en"). Lesky will attempt to parse that 2-character code and work in your system's language. If that variable is not set, it's easy enough to specify:

> LANG="es" les --ayudar

```
el uso de: les [path] [options]

options:
    -h,	--ayuda	Imprime este menú de ayuda
    -i,	--init	Init lesky en el espacio de trabajo especificado por la ruta por defecto cwd [[cwd]]
    -a,	--host	Dirección [localhost]
    -p,	--puerto	El puerto a utilizar [8080]
        --proto	Protocolo para el uso de [http] ({ http, https, http2 })
        --range	Rango de puertos (en caso de que el puerto se toma). Formato de inicio-final [8000,9000]
        --sslKey	Ruta de acceso de la Clave SSL
        --sslCert	Ruta de acceso a los Certificados SSL
    -o,	--abierto	Abra el navegador. [OS default]
    -w,	--reloj	Reloj para los cambios de los archivos en el directorio [staticDir]

---Fin de Ayudar a---
```

So now, the person who knows Spanish can use the tool, without having to think in English! So, this means `.lesrc` can be written like:

``` 
[{ "puerto": 8080 }] // Here, either "port" or "puerto" will work. English is the fall-back
```

Or, maybe someone speaks French and wants to run the tool. That's easy too:

> LANG="fr" les -ow
```
Options for locale fr does not exist, will attempt to download
res.statusCode 200
listening at: (proto = http, host = localhost, port = 8020)
navigateur ouvert
servir statique dir public
Serveur tous les configs commencé
```

NOTE: some English is still mixed in to the console messages, but it is my hope and understanding that the user will see the key messages being translated and still understand the numbers in the English messages. I also hope that the translations are all correct. This was no easy task, even with the help of AI. I will plan to discuss that adventure in perhaps another article. ([PRs](https://github.com/richardeschloss/les/) are welcome if I translated incorrectly!)

Small digression: I'm most proud of this feature because it was the most challenging, but I think it was definitely worth doing. This is my attempt to challenge the phrase "code in English". I can't imagine how difficult my development workflow would be if I had to think in a non-native language. So hopefully, a world of people will find this useful.

## Conclusion

Lesky was designed to help make your life insanely easier, with regards to serving static content and initializing new projects. Separation of server logic from all other logic was meant to free the user from "server lock-in" if it ever needed to be changed out. Mutli-lingual support was added to make it accessible. I hope you find it helpful.