CLI processes input .txt files, generating corresponding .html files
Tags: beginners, cli, typescript
Published: 2023-09-08 17:20:46

Git Repo: https://github.com/avelynhc/til_tracker

### Overview
- Process input .txt files into .html files.
- Allow the user to specify either a single file or a folder containing multiple files as input. Generate one .html output file for each input file.
- By default, write output file(s) to a ./dist folder. If an existing 'dist' folder exists, it will be removed before generating the latest output.
- Attempt to extract a title from the user's input files. If a title is found, it will be placed as the first line, followed by two blank lines.
- Optionally, allow the user to specify a `--stylesheet` or `-s` URL pointing to a CSS stylesheet to be used in the generation of HTML files.

### Usage
#### Convert .txt file to .html file
```text
./test.txt

This is the first paragraph.

This is the second paragraph.

This is the third paragraph.
```

```sh
~/WebstormProjects/til_tracker $ ts-node src/index.ts -i test.txt
Existing folder was successfully removed
Output folder is successfully created!
test.html is created successfully!
```

```html
./dist/test.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<h1>test</h1>
<body>
    <p>This is the first paragraph.</p>
    
    <p>This is the second paragraph.</p>
    
    <p>This is the third paragraph.</p>
</body>
</html>
```

#### Convert .txt files in a folder to .html files in a folder
```text
./test/test1.txt

This is the test1 txt file of test folder.

This is the test1 txt file of test folder.
```

```text
./test/test2.txt

This is the test2 txt file of test folder.

This is the test2 txt file of test folder.
```

```sh
~/WebstormProjects/til_tracker $ ts-node src/index.ts -i test
Existing folder was successfully removed
Output folder is successfully created!
test1.html is created successfully!
test2.html is created successfully!
```

```html
./dist/test1.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>test1</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<h1>test1</h1>
<body>
    <p>This is the test1 txt file of test folder.</p>
    
    <p>This is the test1 txt file of test folder.</p>
</body>
</html>
```

```html
./dist/test2.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>test2</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<h1>test2</h1>
<body>
    <p>This is the test2 txt file of test folder.</p>
    
    <p>This is the test2 txt file of test folder.</p>
</body>
</html>
```

#### Convert .txt file to .html file with optional `-s` argument
```sh
~/WebstormProjects/til_tracker $ ts-node src/index.ts -i test.txt -s https://cdnjs.cloudflare.com/ajax/libs/tufte-css/1.8.0/tufte.min.css 
Existing folder was successfully removed
Output folder is successfully created!
test.html is created successfully!
```

```html
./dist/test.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tufte-css/1.8.0/tufte.min.css">
</head>
<h1>test</h1>
<body>
    <p>This is the first paragraph.</p>
    
    <p>This is the second paragraph.</p>
    
    <p>This is the third paragraph.</p>
</body>
</html>
```

### TIL while working on this project

#### How to write a command-line tool using TypeScript
In this project, I utilized the Node.js library yargs. It aids in constructing interactive command-line interfaces by parsing arguments. To install it, use `npm install yargs`.

**yargs** was employed as follows in this project:

```typescript
const argv = yargs
    .usage("Usage: $0 -i <txtFilename> or <folderContainingTxtFiles>  [-s <css-link>]")
    .option("i", {
        alias: "input",
        describe: ".txt File Name or folder containing .txt files",
        type: "string",
        demandOption: true,
    })
    .option("s", {
        alias: "stylesheet",
        describe: "Optional CSS Link",
        default: "",
        type: "string",
        demandOption: false,
    })
    .alias("h", "help")
    .alias("v", "version")
    .version(appInfo.name + " " + appInfo.version)
    .help().argv;
```
Refer to https://www.npmjs.com/package/yargs for more information. 

#### Processing files and directories using TypeScript:

##### Check if the folder already exists and return a boolean value indicating its existence.

Syntax:
`fs.existsSync( path );`. 
Parameters:
- path: path of the file.  

Example:
```typescript
import fs from "fs";
fs.existsSync("./dist");
```

##### Create a directory synchronously.
Syntax:
`fs.mkdirSync( path, options );`
Parameters:
- path: The path of the directory. It can be a string, buffer, etc.
- options: An optional parameter that determines how to create the directory, such as recursively, etc.

Example:
```typescript
import fs from "fs";
fs.mkdirSync("./dist");
```

##### Delete a file synchronously.
Syntax:
`fs.rmSync( path, options );`
Parameters:
- path: The path of the file. 
- options: Both 'force' and 'recursive' are boolean values. 'Recursive' specifies whether directory removal is performed recursively.

Example:
```typescript
import fs from "fs";
fs.rmSync("./dist", { recursive: true, force: true });
```

#### Read file. 

##### Read the file and return its content.
Syntax:
`fs.readFileSync( path, options );`. 
Parameters:
- path: The path of the file.
- options: Optional parameter containing the encoding and flag.  

Example:  
```typescript
import readFileFs from "fs";
const data:string = readFileFs.readFileSync(inputPath, "utf8");
```


#### Read the contents of a given directory asynchronously.
Syntax:
`fs.readdir( path, options, callback );`
Parameters:
- path: The path of the directory.
- options: Specifies encoding and includes the withFileTypes option.
- callback: A function that takes err and files as arguments.
 
Example:
```typescript
import readFolderFs from "fs";
readFolderFs.readdir(path, function (err: any, files: any[]) {
    if (err) return console.log(err);
});
```