DIY Blog

2023 Jan 28th

Every year the same in January, I start thinking about trying to restart a blog. But this year I feel like I have a newfound focus of trying to extricate the content I'm creating from the ephemeral Slacks and Instagrams that focus on likes and comments.

Inspiration for replacement

I started off with trying to use Hexo, but it is an awful lot of code for something that I don't need to be this complex. I decided I should probably just write my own, like Evan did.

The end result is a very simple little Typescript and node application using ETA for templates.

Experimenting with ChatGPT

To make things more interesting, I decided to see how useful ChatGPT could be for a small programming exercise like this. Obviously others have been at this before me, but I was curious what errors or omissions I'd run into.

You: Write a typescript program for converting a directory of markdown files into a blog


ChatGPT: Here's a basic example of a TypeScript program that converts a directory of Markdown files into a blog:

import * as fs from 'fs';
import * as marked from 'marked';

const directory = './markdown-files/';

fs.readdir(directory, (err, files) => {
  if (err) {
    console.error('Error reading directory:', err);
    return;
  }

  files.forEach((file) => {
    fs.readFile(`${directory}/${file}`, 'utf8', (err, data) => {
      if (err) {
        console.error('Error reading file:', err);
        return;
      }

      const html = marked(data);

      // Write the HTML to a new file
      fs.writeFile(`./blog/${file}.html`, html, (err) => {
        if (err) {
          console.error('Error writing file:', err);
        } else {
          console.log(`Successfully converted ${file} to HTML`);
        }
      });
    });
  });
});
This program uses the fs (file system) module to read the files in the specified directory (in this case, './markdown-files/') and the marked module to convert the Markdown to HTML. It then writes the HTML to a new file in a directory called './blog/'.

It's important to note that this is just an example and there are many ways to convert markdown to html and to create a blog, this could be improved by adding error handling, adding a server to serve the blog, adding a template engine to improve the design of the blog and many other features, this is just a simple program to get you started.


You: write a typescript program to make an index page for the blog


ChatGPT: Here's an example of a TypeScript program that creates an index page for the blog using the files that were previously converted from Markdown to HTML:

import * as fs from 'fs';

const directory = './blog/';

fs.readdir(directory, (err, files) => {
  if (err) {
    console.error('Error reading directory:', err);
    return;
  }

  let blogPosts = '';
  files.forEach((file) => {
    if (file.endsWith('.html')) {
      const postName = file.replace('.html', '');
      blogPosts += `<li><a href="./${file}">${postName}</a></li>`;
    }
  });

  const indexHTML = `
    <html>
      <head>
        <title>My Blog</title>
      </head>
      <body>
        <h1>My Blog</h1>
        <ul>
          ${blogPosts}
        </ul>
      </body>
    </html>
  `;

  fs.writeFile(`${directory}/index.html`, indexHTML, (err) => {
    if (err) {
      console.error('Error writing file:', err);
    } else {
      console.log('Successfully created index page');
    }
  });
});
This program uses the fs (file system) module to read the files in the "./blog" directory and it iterates through the files and creates a list of links to the blog posts, it then creates a simple HTML template that includes the list of links, and finally it writes the HTML template to a new file called "index.html" in the same "./blog" directory.

It's important to note that this is just an example, you could improve this program by adding error handling, by adding a template engine to improve the design of the index page, by adding some sort of pagination or by adding a search bar and many other features, this is just a simple program to get you started.


You: how do I empty a directory in typescript?


ChatGPT: In TypeScript, you can use the fs (file system) module to empty a directory. Here's an example of how you can do this:

import * as fs from 'fs';

const directory = './example-directory/';

fs.readdir(directory, (err, files) => {
  if (err) {
    console.error('Error reading directory:', err);
    return;
  }

  files.forEach((file) => {
    fs.unlink(`${directory}/${file}`, (err) => {
      if (err) {
        console.error('Error deleting file:', err);
      }
    });
  });
});
This program uses the `fs.readdir()` function to read the contents of the specified directory (in this case, './example-directory/'). It then iterates through the array of files and uses the `fs.unlink()` function to delete each file.

It's important to note that this code is not recursive, it will only delete files in the first level of the directory, if you want to delete all the files including subdirectories you could use fs.rmdirSync(directory, { recursive: true }); or fs.rmdir(directory, { recursive: true }, callback) for async version. Also, it's a good practice to add some error handling, for example, if the directory does not exist or if the user doesn't have permission to delete the files.


You: using typescript, split a file at the line "---"


ChatGPT: You can use the fs (file system) module in TypeScript to read a file and split it at a specific line, such as "---". Here's an example of how you can do this:

import * as fs from 'fs';

const filePath = './example-file.txt';
const splitLine = '---';

fs.readFile(filePath, 'utf8', (err, data) => {
  if (err) {
    console.error('Error reading file:', err);
    return;
  }

  const lines = data.split('\n');
  let splitIndex = -1;

  for (let i = 0; i < lines.length; i++) {
    if (lines[i] === splitLine) {
      splitIndex = i;
      break;
    }
  }

  if (splitIndex === -1) {
    console.error(`Split line "${splitLine}" not found in file`);
    return;
  }

  const firstPart = lines.slice(0, splitIndex).join('\n');
  const secondPart = lines.slice(splitIndex + 1).join('\n');

  console.log('First part of file:', firstPart);
  console.log('Second part of file:', secondPart);
});
This program uses the `fs.readFile()` function to read the contents of the specified file (in this case, './example-file.txt'). It then splits the contents of the file by new lines using `data.split('\n')`. It then iterates through the array of lines and looks for the line containing the specified split line "---", when it finds it, it saves the line index. Then, it creates two variables `firstPart` and `secondPart` using the `slice()` method, the first part of the file is from the start of the file up to the split line, the second part of the file is from the split line to the end of the file.

It's important to note that this code is assuming that the split line is only used once in the file, if the file has multiple "---" on different lines it will only split at the first occurrence. Also, it's a good practice to add some error handling, for example, if the file does not exist or if the user doesn't have permission to read the file.


You: use 'fs/promises' instead.


ChatGPT: Sure, you can use the fs/promises module in TypeScript to read a file and split it at a specific line, such as "---". Here's an example of how you can do this:

import * as fs from 'fs/promises';

const filePath = './example-file.txt';
const splitLine = '---';

async function splitFile() {
  try {
    const data = await fs.readFile(filePath, 'utf8');
    const lines = data.split('\n');
    let splitIndex = -1;

    for (let i = 0; i < lines.length; i++) {
      if (lines[i] === splitLine) {
        splitIndex = i;
        break;
      }
    }

    if (splitIndex === -1) {
      console.error(`Split line "${splitLine}" not found in file`);
      return;
    }

    const firstPart = lines.slice(0, splitIndex).join('\n');
    const secondPart = lines.slice(splitIndex + 1).join('\n');

    console.log('First part of file:', firstPart);
    console.log('Second part of file:', secondPart);
  } catch (err) {
    console.error('Error reading file:', err);
  }
}

splitFile();
This program uses the `fs.promises.readFile()` function to read the contents of the specified file (in this case, './example-file.txt'). It then splits the contents of the file by new lines using `data.split('\n')`. It then iterates through the array of lines and looks for the line containing the specified split line "---", when it finds it, it saves the line index. Then, it creates two variables `firstPart` and `secondPart` using the `slice()` method, the first part of the file is from the start of the file up to the split line, the second part of the file is from the split line to the end of the file.

It's important to note that this code is assuming that the split line is only used once in the file, if the file has multiple "---" on different lines it will only split at the first occurrence. Also, it's a good practice to add some error handling, for example, if the file does not exist or if the user doesn't have permission to read the file. It's worth mentioning that, fs/promises is an experimental feature and is not a stable API, it might change in the future.


You: parse yaml in typescript


ChatGPT: You can parse YAML in TypeScript using a YAML parsing library, such as js-yaml. To use js-yaml in TypeScript, you will first need to install it using npm:

npm install js-yaml
Once you have installed the library, you can use the `js-yaml` module in your TypeScript code as follows:
import * as yaml from 'js-yaml';

const yamlText = `
name: John Doe
age: 33
`;

const data = yaml.safeLoad(yamlText);

console.log(data);
This code will parse the YAML string `yamlText` and store the resulting object in the `data` variable. You can then access the properties of the object as you would with any other JavaScript object.

The js-yaml.safeLoad() method is used here because it is the safest method for parsing YAML. It will only parse a limited set of YAML constructs, and will raise an exception if it encounters any malicious constructs. There are other methods available in the js-yaml library that provide more relaxed parsing options, such as js-yaml.load(), but these should be used with caution to avoid security vulnerabilities.


You: js-yaml doesn't have a safeLoad method.


ChatGPT: My apologies, you are correct. The safeLoad method is not available in js-yaml. To parse a YAML string with js-yaml safely, you can use the load method and specify the schema that should be used to parse the string. The FAILSAFE_SCHEMA is a safe schema that only allows a limited set of YAML constructs. Here is an example of how to use the load method and the FAILSAFE_SCHEMA in TypeScript:

import * as yaml from 'js-yaml';

const yamlText = `
name: John Doe
age: 33
`;

const data = yaml.load(yamlText, { schema: yaml.FAILSAFE_SCHEMA });

console.log(data);
This code will parse the YAML string `yamlText` and store the resulting object in the `data` variable. The `{ schema: yaml.FAILSAFE_SCHEMA }` option specifies that the `FAILSAFE_SCHEMA` should be used to parse the string, ensuring that only a limited set of safe YAML constructs are allowed.

I apologize again for the error in my previous answer.


You: sort string dates in typescript


ChatGPT: In TypeScript, you can sort an array of dates that are represented as strings by converting the strings to Date objects and then sorting the array. Here's an example of how you can do this:

const dates = ['2022-12-30', '2023-01-01', '2022-12-29'];

const sortedDates = dates.sort((a, b) => {
  const dateA = new Date(a);
  const dateB = new Date(b);
  return dateA.getTime() - dateB.getTime();
});

console.log(sortedDates); // Output: [ '2022-12-29', '2022-12-30', '2023-01-01' ]
In this example, the `sort` method is called on the `dates` array, and a comparison function is passed as an argument. The comparison function takes two arguments, `a` and `b`, which represent the two elements being compared. The function creates `Date` objects for `a` and `b` using the `new Date(...)` constructor, and then returns the difference between the timestamps of the two `Date` objects. The `sort` method will sort the `dates` array in ascending order based on the comparison function, so that the earliest dates come first.

You: print yaml dates using moment in typescript


ChatGPT: You can use the Moment.js library in TypeScript to parse and format dates stored in a YAML file. Here's an example of how you can do this:

import * as yaml from 'js-yaml';
import * as moment from 'moment';

const yamlText = `
created_at: 2022-12-30
updated_at: 2023-01-01
`;

const data = yaml.load(yamlText);

console.log(moment(data.created_at).format('MMMM Do YYYY')); // Output: December 30th 2022
console.log(moment(data.updated_at).format('MMMM Do YYYY')); // Output: January 1st 2023
In this example, the YAML string `yamlText` is loaded into the `data` object using the `js-yaml.load()` method. The dates `created_at` and `updated_at` are then passed to the `moment()` function, which returns a Moment object that can be used to format the date. The `format()` method is called on the Moment object, passing in a string that specifies the desired date format. In this case, the format is `MMMM Do YYYY`, which will output the month name, the day of the month, and the year.

You: typescript get a files creation date


ChatGPT: In TypeScript, you can use the Node.js fs module to get the creation date of a file. Unfortunately, the fs module does not provide a way to directly retrieve the creation date of a file. You can, however, retrieve the file's ctime (change time) and birthtime (creation time) attributes, which provide timestamps that are close to the file's creation date. Here's an example of how you can use the fs module to get the birthtime of a file in TypeScript:

import * as fs from 'fs';
import * as path from 'path';

const filePath = path.join(__dirname, 'file.txt');

fs.stat(filePath, (error, stats) => {
  if (error) {
    console.error(error);
    return;
  }

  const creationDate = stats.birthtime;
  console.log(creationDate);
});
In this example, the `fs.stat` method is used to retrieve the file statistics for the file located at `filePath`. The `stats` object that is returned by `fs.stat` contains the file's `birthtime` attribute, which represents the file's creation date. The creation date is logged to the console.

Keep in mind that the accuracy of the birthtime attribute may vary depending on the file system and operating system. In some cases, the birthtime attribute may not be set, so it's important to check for its presence before using it.


You: can i use fs/promises?


ChatGPT: Yes, you can use the fs/promises module in TypeScript to get the creation date of a file. The fs/promises module provides an asynchronous, promise-based API for working with the file system, and it can be used to retrieve the birthtime of a file. Here's an example of how you can use the fs/promises module to get the birthtime of a file in TypeScript:

import * as fs from 'fs/promises';
import * as path from 'path';

async function getCreationDate(filePath: string): Promise<Date> {
  const stats = await fs.stat(filePath);
  return stats.birthtime;
}

const filePath = path.join(__dirname, 'file.txt');

getCreationDate(filePath)
  .then(creationDate => {
    console.log(creationDate);
  })
  .catch(error => {
    console.error(error);
  });
In this example, an asynchronous function `getCreationDate` is defined, which uses the `fs/promises.stat` method to retrieve the file statistics for the file located at `filePath`. The `stats` object that is returned by `fs/promises.stat` contains the file's `birthtime` attribute, which represents the file's creation date. The creation date is logged to the console.

Keep in mind that the accuracy of the birthtime attribute may vary depending on the file system and operating system. In some cases, the birthtime attribute may not be set, so it's important to check for its presence before using it.