davideisinger.com

My personal website
Log | Files | Refs | README

index.md (5635B)


      1 ---
      2 title: "Spellcheck Your Hugo Site With CSpell"
      3 date: 2024-11-20T18:03:32-05:00
      4 draft: false
      5 tags:
      6 - meta
      7 references:
      8 - title: "The Static Site Paradox | Loris Cro's Blog"
      9   url: https://kristoff.it/blog/static-site-paradox/
     10   date: 2024-10-31T03:33:40Z
     11   file: kristoff-it-edtlns.txt
     12 ---
     13 
     14 I edit these posts pretty carefully before publishing, but I inevitably find a misspelling or two after the fact. In the spirit of continuous improvement, I decided to see what kind of automated solutions are out there for spellchecking Markdown files, and found [CSpell][1]. It works well, but its default configuration found a ton of false positives that I had to scroll past to find the actual errors.
     15 
     16 [1]: https://cspell.org/
     17 
     18 <!--more-->
     19 
     20 Fortunately, it's quite configurable, and I've gotten it to where it only flags actual misspelled words. Here's how.
     21 
     22 ### 1. Install CSpell
     23 
     24 Assuming a modern version of Node.js (≥18), you can use [npx][2] to download and run CSpell in a single command:
     25 
     26 ```sh
     27 npx cspell content/**/*.md
     28 ```
     29 
     30 You'll see a ton of spelling errors -- ignore them for now.
     31 
     32 [2]: https://docs.npmjs.com/cli/v10/commands/npx
     33 
     34 ### 2. Add config file
     35 
     36 Next, let's create a basic config file. In the root of your site, put the following in `.cspell.json`:
     37 
     38 ```json
     39 {
     40   "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
     41   "version": "0.2",
     42   "dictionaries": [
     43     "english"
     44   ]
     45 }
     46 ```
     47 
     48 ### 3. Add additional languages
     49 
     50 My site (especially the stuff in [/elsewhere][3] that I've mirrored from my company's website) has code snippets that the English dictionary doesn't recognize. Fortunately, CSpell ships with a bunch of [additional dictionaries][4]. Adding `"ruby"`, `"golang"`, and `"java"` to the `"dictionaries"` array makes a bunch of misspellings go away.
     51 
     52 [3]: /elsewhere/
     53 [4]: https://github.com/streetsidesoftware/cspell-dicts/tree/main/dictionaries
     54 
     55 ### 4. Ignore front matter
     56 
     57 This one may or may not apply to your site, so feel free to ignore, but I see a lot of false positives in the [front matter][5] of my posts, mostly around the lists of [references][6]. To ignore the front matter section entirely, add the following to your config file (credit to [this helpful GitHub comment][7]):
     58 
     59 ```json
     60 "patterns": [
     61   {
     62     "name": "front_matter",
     63     "pattern": "/^(-{3}|[+]{3})$(\\s|\\S)*?^\\1$/gm"
     64   }
     65 ],
     66 "languageSettings": [
     67   {
     68     "languageId": "markdown",
     69     "ignoreRegExpList": [
     70       "front_matter",
     71     ]
     72   }
     73 ]
     74 ```
     75 
     76 Note that you'll no longer catch misspellings in post titles, so it might make sense to use a more targeted regular expression.
     77 
     78 [5]: https://gohugo.io/content-management/front-matter/
     79 [6]: https://git.sr.ht/~dce/davideisinger.com/tree/main/item/content/journal/dispatch-21-november-2024/index.md?view-source#L7-11
     80 [7]: https://github.com/streetsidesoftware/cspell/discussions/3456#discussioncomment-3438647
     81 
     82 ### 5. Ignore proper nouns
     83 
     84 I also see a lot of proper nouns being flagged as misspellings, so I decided to just ignore any word that begins with a capital letter. Create a new entry in the `"patterns"` array:
     85 
     86 ```json
     87 {
     88   "name": "proper_nouns",
     89   "pattern": "/[\\W_][A-Z][\\S]+/g"
     90 }
     91 ```
     92 
     93 That's any non-word character (or an underscore), followed by a capital letter, followed by one or more non-space characters. I'm sure that's not perfect, but it's good enough for my content. Add the new pattern to the `"ignoreRegExpList"`:
     94 
     95 ```json
     96 "languageSettings": [
     97   {
     98     "languageId": "markdown",
     99     "ignoreRegExpList": [
    100       "front_matter",
    101       "proper_nouns"
    102     ]
    103   }
    104 ]
    105 ```
    106 
    107 ### 6. Fix spelling
    108 
    109 Now comes the hard part: run CSpell again (`npx cspell content/**/*.md`), look at all the misspellings it finds, and fix all the ones you consider to be valid. Computers can't help us here, friend.
    110 
    111 ### 7. Create a custom dictionary
    112 
    113 Now we'll add all the unrecognized words to a custom dictionary so that CSpell will stop flagging them. First, create the list of words:
    114 
    115 ```sh
    116 npx cspell --words-only --unique content/**/*.md | sort > .dictionary
    117 ```
    118 
    119 Then add a new `"dictionaryDefinitions"` array in your config file:
    120 
    121 ```json
    122 "dictionaryDefinitions": [
    123   {
    124     "name": "exceptions",
    125     "path": ".dictionary",
    126     "addWords": true
    127   }
    128 ],
    129 ```
    130 
    131 Finally, add `"exceptions"` to the `"dictionaries"` array. At this point, CSpell should find zero misspellings. To add new exceptions to the list in the future, you can run:
    132 
    133 ```sh
    134 npx cspell --words-only --unique content/**/*.md >> .dictionary
    135 sort -o .dictionary .dictionary
    136 ```
    137 
    138 ### 8. Add to build pipeline
    139 
    140 With all this stuff set up, it's dead simple to add spellchecking to the build pipeline to ensure you never publish misspellings. As long as your job runner has `npx` available, you can just run the same `npx cspell content/**/*.md` command you've been running locally in a build step. [Here's where I do it.][8]
    141 
    142 [8]: https://git.sr.ht/~dce/davideisinger.com/tree/main/item/.build.yml#L27-29
    143 
    144 ---
    145 
    146 [Here's the final `.cspell.json` config file.][9] I'm super happy with this setup -- it's already catching misspellings in the process of writing these words. I'm reminded of [a post][10] I read a few weeks ago, about the irony of how good and simple website publishing has become for technical people, and how complex it is for the less technically-inclined. Imagine trying to accomplish this same functionality in a typical CMS -- [it would not work well, if it worked at all][11].
    147 
    148 [9]: https://git.sr.ht/~dce/davideisinger.com/tree/main/item/.cspell.json
    149 [10]: https://kristoff.it/blog/static-site-paradox/
    150 [11]: https://wordpress.org/support/topic/garbage-170/