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/