nanowrimo 2020: update 2

2020-11-02

 | 

~4 min read

 | 

652 words

This month I’ll be writing less code (in my free time) as I try to write a 50,000 word novel in 30 days. Odds are not in my favor, but one thing I can do to help myself is … keep track of how much I’ve written.

One easy way to do that would be to use a fully featured text editor. There are a ton of great ones. Ulysses, Scrivener, even Word. All of these (and so many more) have the very basic function of a word count.

I’ve been writing in markdown for the past few years and earlier this year moved entirely to pure markdown (migrating away from Bear when my database grew to 7gb and the app became unbearably slow on my phone). This is actually the motivation for nom, my CLI application. Markdown is lovely to writing, but a single directory of markdown files is hard to manage - particularly as it grows. Writing this novel won’t be any different - I’ll have chapter files, scratch files, plot files, character files, etc. Worse, it’s the first time I’ve ever attempted something like this so I’m bound to organize it wrong. That’s just the rule.

Coming back to the topic at hand - what does any of this have to do with NaNoWriMo? Well, I have a word count goal (which as every engineer knows is a terrible way to ensure quality, but I digress), so, I wrote a word-counter. Or more specifically, I used Jon Schlinkert’s1 wordcount package in a node script to count all of the words in my project directory.

It’s not the most elegant approach, and I know that I could improve this by making it asynchronous, but given the size of the project, synchronous feels fine:

wordcount.js
const fs = require("fs")
const path = require("path")
const wordCount = require("wordcount")

function findAllMdFiles(directoryPath) {
  const mdFilePaths = []
  const dirQueue = [directoryPath]

  while (dirQueue.length) {
    const dir = dirQueue.pop()
    const dirContents = fs.readdirSync(dir)
    dirContents.forEach((el) => {
      const elPath = path.resolve(dir, el)
      const stat = fs.statSync(elPath)
      if (stat && stat.isDirectory()) {
        dirQueue.push(elPath)
      } else if (stat && path.extname(elPath) === ".md") {
        mdFilePaths.push(elPath)
      }
    })
  }
  return mdFilePaths
}

function count(filePath) {
  const file = fs.readFileSync(filePath, { encoding: "utf-8" })
  return wordCount(file)
}

function countAllWords() {
  const fileList = findAllMdFiles(path.resolve(__dirname, "src"))
  const totalWordCount = fileList.reduce(
    (acc, filePath) => acc + count(filePath),
    0,
  )
  console.log(`|${new Date().toISOString()} | ${totalWordCount}|`)
  return totalWordCount
}

countAllWords()

The cool part about this is that I created a file wordcount.md in the root of the project and a yarn script to run it in my package.json:

package.json
{
  "scripts": {
    "wordcount": "node ./wordcounter.js >> wordcountlog.md"
  }
}

Now, every time I save, I just run yarn wordcount and I to see my progress toward 50k in a pretty table format, e.g.:

Date Word count
2020-10-31T20:23:58.010Z 0
2020-10-31T20:55:48.495Z 582
2020-10-31T22:29:38.317Z 1390

Note: I’m being generous. All words in my src folder are counted, including scratch notes. If I ever want to be stricter, I can simply update the target directory from src to something more appropriate - however, given my confidence above that I’ll have to reorganize my files later, I thought it okay to start by casting a wide net.

This was totally unnecessary. I could have used one of the many wonderful programs out there specifically designed to write - but I don’t need all of the bells and whistles. I just needed a word count. And one of the really amazingly cool things about being an engineer is that I could just build what I needed. How cool is that?!

Footnotes

  • 1 Jon’s prolific and is behind multiple projects I’ve used elsewhere, most notably enquirer which plays a key role in my refactoring of nom.


Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!