generator-mfgames-writing v0.3.2

Related to process of writing from a few days ago, I've been working on another tool that I find myself using fairly often: Yeoman. This is a scaffolding tool, which basically means it asks a few questions and then sets up a project. In my case, it does a lot of the drudge work of creating a writing project and getting it ready for Gitlab's CI which I also use heavily (and why I wrote commitlint-gitlab-ci).

Like many of the writing tools, this is based on Javascript (but not Typescript this time).

$ sudo npm install -g yo generator-mfgames-writing

The first thing it does is ask a number of questions about the project. I'll use Nor Curse Be Found, my Beauty and the Beast sequel, as an example.

$ mkdir nor-curse-be-found
$ cd nor-curse-be-found
$ yo mfgames-writing
? The title for your novel or story: (Nor Curse Be Found)

All of my writing projects have a unique “slug” to describe them. This is used for the website's URL, such as, and also the name of the output (dmoonfire-nor-curse-be-found-0.0.1.epub). I use a library to pull out the current directory's name and try to make a reasonable title out of it. I can change the title to something more accurate, maybe with apostrophes or possessives, but generally I keep the slug pretty even. In this case, the parenthetical item is the suggested output, so I can just hit return.

? The title for your novel or story: Nor Curse Be Found
? The slug based on the title: (nor-curse-be-found)

Just in case the directory doesn't match the directory, I use a library to take that given title and create a slug out of it. One of the biggest things, my Fast Trip rule, is that I don't want to tell someone how to work. That is why I called this semi-opinionated in that there are a lot of options but the default is how I work.

There is also a question about bylines, but then we start to get into the technical bits.

? The title for your novel or story: Nor Curse Be Found
? The slug based on the title: nor-curse-be-found
? Author's name or byline: D. Moonfire
? Do you want to create the Git repository? (Y/n)

This came from some years ago, but I do one story per repository, more so if I think the story has a possibility of being a bigger piece (which many of mine are), I want to track the versions independently, or I might be sending to an editor or submitting it somewhere (not likely these days, I self-censor a lot there). Since this is so common, I put it in as part of the questions.

I have a slowly evolving set of practices when it comes to writing processes. One of the first ones is setting up a Git repository. This always starts with configuring husky, semantic-release, conventional-commits, and commitlint. (I'm working on updating my Yeoman generator to make this a lot easier, but that will be later this week.)

? Do you want to create the Git repository? Yes
? Any command to run after `git init`? set-moonfire-git-user

I also write for a number of different projects (and bylines) which means I don't always use the same Gitlab login or even email. To faciliate this, I do not have a global and set up in Git so it blows up if I try to make a commit without setting a local one. Since that is tedious, I write a bunch of scripts that set the user information and SSH keys (set-moonfire-git-user for writing, set-mfgames-git-user for programming for example) and I want to call them before I do that first commit. If I just hit return to have a blank, then nothing extra will happen.

? Any command to run after `git init`? set-moonfire-git-user
? Do you want to create the initial commit? Yes
? Do you want to create the initial Git tag? Yes
? What version do you want to start? 0.0.1

Now, the initial commit is a messy one and a philosophical difference between me and the Semantic Release folks. They have their reasons to hard-code the value to “1.0.0” but I'm specifically start this process before a release, so I feel it should be “0.0.1” because I want that tracking as soon as possible.

Since I don't have the ability to set it via a configuration file, I get around it by creating the initial commit and then allowing it to be tagged so it can be pushed up and get the starting version I want.

This is also because I tag versions I give to editors, beta readers, and even my writing group (when I went to it). The tagged version also shows up on the various Fedran pages and mainly it shows progression as I write.

So, Fast Trip so I jump through a few hoops. It should also be clear why I needed that command to run after git init to set my user, otherwise this part would blow up with a “user not set” message.

? What version do you want to start? 0.0.1
? Which package manager do you use? (Use arrow keys)

Moving away from Git and into the build process, the first question is package management. I have a love/hate relationships with npm. The command works great, but I've been struggling with it (more on than off lately) so I've been using yarn fairly heavily in the last year or so. But, with Yeoman I can give both so the user can use up or down keys to select and hit return to choose. Once they do, the menu goes away and is replaced with the choice.

? Which package manager do you use? Yarn
? Do you want to use semantic releases? Yes
? Do you want to use conventional commits? Yes
? Do you want to use Husky? Yes

The next three are to set up the common functionality I use with releases. They set up semantic-release, conventional commits, commitlint, and Husky which I use to make sure all the commit messages are clean. These four program have been a stable in most of my development lately, for coding and writing, for Typescript and even C#.

? Do you want to use Husky? Yes
? Do you want to use CI/CD? Yes
? Do you use Gitlab for your CI/CD? Yes

I can't say enough for Gitlab's CI. Whenever I push up code to the server, it runs the entire publication process. Assuming it works, it then produces a zip archive with EPUB, MOBI, PDF, and other formats without me doing anything else. Every single time. With sematic releases and the rest, each one has a unique version so I don't have “final”, “final-2”, “final-2a”, etc. Just a clean version number that shows up everywhere, starting at 0.0.1.

? Do you use Gitlab for your CI/CD? Yes
? Do you use asdf? No

This is another tool I use, asdf. This was also something that pretty much started last year, but one of the goals of mfgames-writing was to be able to produce consistent output of novels even years later. Well, what I didn't realize is how much the Node ecosystem would change and Node 15 doesn't exactly run Node 8 packages very well due to deprecations and changing libraries.

This is where asdf comes in. It puts a small file (.tool-versions) which says which version of Node, Yarn, C#, Python, whatever and will automatically change to those versions when I cd into the directory. It basically rolls in all the functionality of nvm, virtualenv, and a bunch of other localized version libraries into one. Since I write and work across many languages, having one tool to manage that is easier for me. So, saying “yes” to the question copies that .tool-versions into the resulting directory.

? Do you use asdf? Yes
? Which formats do you want to use? (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ PDF

With this next question, we are out of the build section and into the formats. Basically, this just queries which types of files will be written out. The directions are pretty clear, but basically this will determine which of the format libraries are included.

? Which formats do you want to use? PDF, EPUB, DOCX, HTML
? The name of the theme package to use: (@mfgames-writing/clean-theme)

I only have three themes right now, two are “generic” and one I prefer just for Fedran stories (but obviously don't prevent anyone from using it). Later, I hope to have more themes, but basically the choices are @mfgames-writing/clean-theme and @mfgames-writing/greekil-theme (a Gentium-based theme).

? The name of the theme package to use: @mfgames-writing/clean-theme
Creating initial Git repository
Initialized empty Git repository in fedran/nor-curse-be-found/.git/
Running custom Git command

I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.

   create package.json
    force ../../../.yo-rc-global.json
    force .yo-rc.json
   create release.config.js
   create commitlint.config.js
   create .husky/commit-msg
   create .gitlab-ci.yml
   create .tool-versions
   create publication.yaml
   create yarn.lock
   create .gitignore
   create chapters/

Changes to package.json were detected.

Running yarn install for you to install the required dependencies.

With that, it just producing a wall of tell as it creates all the files, hooks up everything, and basically gets my entire project ready, including a sample first chapter, a really basic, and a Git repository ready to push.

$ ls -a
.                     .git            node_modules
..                    .gitignore      package.json       release.config.js
chapters              .gitlab-ci.yml  package-lock.json  .yo-rc.json
commitlint.config.js  .husky          publication.yaml

Right off the bat, I can build the files and see the output.

$ yarn build:pdf
... lots of output
$ ls *.pdf
$ yarn build
... huge amounts of output
$ ls nor-curse-be-found*
nor-curse-be-found-0.0.1.docx  nor-curse-be-found-0.0.1.epub
nor-curse-be-found-0.0.1.html  nor-curse-be-found-0.0.1.pdf

I'm also ready to push up to Gitlab.

$ git remote add origin
$ git push --tags
$ git push -u origin main

Hopping over to Gitlab, I can see it building immediately.

Initial CI Run

As soon as it is done, it uploads the output as an artifact.

End of Log

I can then click on the browse (or download) button on the right.

Browse and Download Buttons

Browsing lets me see a list of all the outputs that I've added.

Browse Artifacts

And I can even click on the PDF to preview it.

View PDF