book-kit 0.0.0-stage → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +72 -0
  3. package/README.md +251 -2
  4. package/bin/book-kit.js +115 -0
  5. package/commands/_emoji_io.py +32 -0
  6. package/commands/audit-chapter-lengths.js +244 -0
  7. package/commands/check-epub-size.js +68 -0
  8. package/commands/check-pdf-page-count.js +162 -0
  9. package/commands/composite-kindle-emoji.py +85 -0
  10. package/commands/composite-print-emoji.py +98 -0
  11. package/commands/conventions.js +76 -0
  12. package/commands/convert-pdfx.sh +194 -0
  13. package/commands/download-icc-profile.sh +105 -0
  14. package/commands/fonts.py +212 -0
  15. package/commands/generate-reference-docx.py +344 -0
  16. package/commands/kindle-fonts.js +173 -0
  17. package/commands/links-to-footnotes.js +366 -0
  18. package/commands/markdownlint.js +25 -0
  19. package/commands/minify-css.js +43 -0
  20. package/commands/optimize-images.js +73 -0
  21. package/commands/parity.sh +150 -0
  22. package/commands/postprocess-epub.js +541 -0
  23. package/commands/prep-print-src.js +77 -0
  24. package/commands/prose.js +38 -0
  25. package/commands/release-files.js +89 -0
  26. package/commands/reorder-print-frontmatter.js +79 -0
  27. package/commands/strip-page-rules.js +1168 -0
  28. package/commands/strip-property-rules.js +93 -0
  29. package/commands/update-key-takeaways.js +314 -0
  30. package/commands/validate.js +283 -0
  31. package/configs/markdownlint-cli2.jsonc +98 -0
  32. package/configs/textlintrc.json +22 -0
  33. package/css/theme/01-tokens.css +83 -0
  34. package/css/theme/02-fonts.css +79 -0
  35. package/css/theme/03-prose.css +53 -0
  36. package/css/theme/04-base.css +226 -0
  37. package/css/theme/05-components.css +546 -0
  38. package/css/theme/06-utilities.css +40 -0
  39. package/css/theme/07-ereader.css +245 -0
  40. package/css/theme/08-epub.css +865 -0
  41. package/css/theme/09-back-matter.css +47 -0
  42. package/css/theme/10-dark.css +302 -0
  43. package/css/theme/11-print.css +1229 -0
  44. package/css/theme.css +38 -0
  45. package/defaults/book.config.yml +318 -0
  46. package/defaults/pandoc-base.yml +19 -0
  47. package/defaults/pandoc-docx.yml +6 -0
  48. package/defaults/pandoc-epub-fonts.yml +6 -0
  49. package/defaults/pandoc-epub.yml +15 -0
  50. package/defaults/pandoc-kindle.yml +6 -0
  51. package/emoji/1f382.png +0 -0
  52. package/emoji/1f389.png +0 -0
  53. package/emoji/1f440.png +0 -0
  54. package/emoji/1f44d.png +0 -0
  55. package/emoji/1f44f.png +0 -0
  56. package/emoji/1f454.png +0 -0
  57. package/emoji/1f4a1.png +0 -0
  58. package/emoji/1f4bb.png +0 -0
  59. package/emoji/1f602.png +0 -0
  60. package/emoji/1f603.png +0 -0
  61. package/emoji/1f604.png +0 -0
  62. package/emoji/1f60a.png +0 -0
  63. package/emoji/1f60f.png +0 -0
  64. package/emoji/1f64c.png +0 -0
  65. package/emoji/1f64f.png +0 -0
  66. package/emoji/1f914.png +0 -0
  67. package/emoji/1f923.png +0 -0
  68. package/emoji/2714.png +0 -0
  69. package/emoji/274c.png +0 -0
  70. package/filters/add-div-titles.lua +196 -0
  71. package/filters/arc-notice.lua +36 -0
  72. package/filters/body-emoji-images.lua +48 -0
  73. package/filters/callout-classes.lua +40 -0
  74. package/filters/lib/book_kit.lua +110 -0
  75. package/filters/revision.lua +17 -0
  76. package/filters/strip-comments.lua +40 -0
  77. package/filters/strip-emoji-kindle.lua +134 -0
  78. package/fonts/LICENSE-source-code-pro.md +93 -0
  79. package/fonts/LICENSE-source-sans.md +93 -0
  80. package/fonts/LICENSE-source-serif.md +93 -0
  81. package/fonts/recipe.json +41 -0
  82. package/just/book-kit.just +229 -0
  83. package/lib/acronyms.js +47 -0
  84. package/lib/callouts.js +17 -0
  85. package/lib/config.js +211 -0
  86. package/lib/is-main.js +21 -0
  87. package/lib/lint.js +148 -0
  88. package/lib/pandoc.js +148 -0
  89. package/lib/paragraphs.js +173 -0
  90. package/lib/revision.js +35 -0
  91. package/lib/structure.js +39 -0
  92. package/lib/textlint-github-formatter.js +58 -0
  93. package/lib/validate.js +202 -0
  94. package/package.json +98 -4
  95. package/presets/business-nonfiction/CONVENTIONS.md +35 -0
  96. package/presets/business-nonfiction/preset.yml +128 -0
  97. package/profiles/pdfx-def.ps +42 -0
  98. package/requirements.txt +14 -0
  99. package/rules/no-ai-like-patterns.js +209 -0
  100. package/rules/no-assumptive.js +76 -0
  101. package/rules/no-bare-acronyms.js +129 -0
  102. package/rules/no-cliches.js +544 -0
  103. package/rules/no-cultural-idioms.js +422 -0
  104. package/rules/no-vague-intensifiers.js +94 -0
  105. package/templates/template-epub.html +79 -0
  106. package/templates/template.html +73 -0
  107. package/toolchain.json +42 -0
  108. package/validators/acronym-expansion.js +94 -0
  109. package/validators/bold-keywords.js +56 -0
  110. package/validators/callout-balance.js +74 -0
  111. package/validators/callout-divs.js +66 -0
  112. package/validators/callout-labels.js +102 -0
  113. package/validators/chapter-lengths.js +14 -0
  114. package/validators/chapter.js +57 -0
  115. package/validators/cross-references.js +45 -0
  116. package/validators/doubled-words.js +76 -0
  117. package/validators/duplicate-headings.js +52 -0
  118. package/validators/em-dashes.js +73 -0
  119. package/validators/escaped-smallcaps.js +46 -0
  120. package/validators/footnotes.js +78 -0
  121. package/validators/heading-hierarchy.js +58 -0
  122. package/validators/hypothetical-hooks.js +103 -0
  123. package/validators/index-integrity.js +28 -0
  124. package/validators/index.js +73 -0
  125. package/validators/list-punctuation.js +123 -0
  126. package/validators/min-opener-length.js +87 -0
  127. package/validators/no-bare-url-link-text.js +38 -0
  128. package/validators/no-todo-markers.js +49 -0
  129. package/validators/no-transition-todos.js +34 -0
  130. package/validators/number-range-dashes.js +72 -0
  131. package/validators/paragraph-length.js +62 -0
  132. package/validators/quote-punctuation.js +85 -0
  133. package/validators/section-intro-final-lead.js +90 -0
  134. package/validators/section-intro-has-lead.js +41 -0
  135. package/validators/section-intro-no-tldr.js +37 -0
  136. package/validators/section-intros.js +33 -0
  137. package/validators/sentence-starters.js +132 -0
  138. package/validators/smallcaps.js +67 -0
  139. package/validators/tldr-length.js +58 -0
@@ -0,0 +1,422 @@
1
+ /**
2
+ * textlint rule: no-cultural-idioms
3
+ *
4
+ * Detects culturally specific idioms that may confuse a global audience.
5
+ * Readers who learned English as a second or third language, or who read
6
+ * literally, can't decode an idiom they've never heard.
7
+ *
8
+ * Catches:
9
+ * - American sports idioms (baseball, football, basketball)
10
+ * - Poker/gambling idioms
11
+ * - Military idioms
12
+ * - Region-specific idioms (British, American colloquialisms)
13
+ * - Food/domestic idioms opaque to non-native speakers
14
+ */
15
+
16
+ const PATTERNS = [
17
+ // === American sports idioms ===
18
+ {
19
+ pattern: /\bhome run\b/i,
20
+ message: '"Home run" is a baseball idiom — use "major success", "big win", or describe the achievement.'
21
+ },
22
+ {
23
+ pattern: /\bknock(?:ed|ing)? (?:it )?out of the park\b/i,
24
+ message: '"Knock it out of the park" is a baseball idiom — use "exceeded expectations" or describe the success.'
25
+ },
26
+ {
27
+ pattern: /\btouch base\b/i,
28
+ message: '"Touch base" is a baseball idiom — use "check in", "follow up", or "connect".'
29
+ },
30
+ {
31
+ pattern: /\bcurveball\b/i,
32
+ message: '"Curveball" is a baseball idiom — use "unexpected challenge", "surprise", or "complication".'
33
+ },
34
+ {
35
+ pattern: /\btee-?ball\b/i,
36
+ message: '"Tee-ball" is an American youth sport — use a universally recognized activity.'
37
+ },
38
+ {
39
+ pattern: /\bright off the bat\b/i,
40
+ message: '"Right off the bat" is a baseball idiom — use "immediately", "from the start", or "right away".'
41
+ },
42
+ {
43
+ pattern: /\bstep up to the plate\b/i,
44
+ message: '"Step up to the plate" is a baseball idiom — use "take responsibility" or "volunteer".'
45
+ },
46
+ {
47
+ pattern: /\bballpark (?:figure|estimate|number)\b/i,
48
+ message: '"Ballpark" is a baseball idiom — use "rough estimate" or "approximate figure".'
49
+ },
50
+ {
51
+ pattern: /\bthree strikes\b/i,
52
+ message: '"Three strikes" is a baseball idiom — use "three attempts" or "multiple failures".'
53
+ },
54
+ {
55
+ pattern: /\bhail mary\b/i,
56
+ message: '"Hail Mary" is an American football idiom — use "desperate attempt" or "long shot".'
57
+ },
58
+ {
59
+ pattern: /\bmoving the goalposts?\b/i,
60
+ message: '"Moving the goalposts" is a sports idiom — use "changing the criteria" or "shifting expectations".'
61
+ },
62
+ {
63
+ pattern: /\bdrop(?:ped|ping)? the ball\b/i,
64
+ message: '"Drop the ball" is a sports idiom — use "failed to follow through", "missed", or "neglected".'
65
+ },
66
+ {
67
+ pattern: /\bpunt(?:ed|ing)?\b(?! (?:on|return))/i,
68
+ message: '"Punt" is an American football idiom — use "defer", "postpone", or "skip".'
69
+ },
70
+ {
71
+ pattern: /\bslam dunk\b/i,
72
+ message: '"Slam dunk" is a basketball idiom — use "easy win", "obvious choice", or "guaranteed success".'
73
+ },
74
+ {
75
+ pattern: /\blevels? the playing field\b/i,
76
+ message: '"Level the playing field" is a sports idiom — use "create equal footing", "remove barriers", or "equalize opportunity".'
77
+ },
78
+
79
+ // === Poker/gambling idioms ===
80
+ {
81
+ pattern: /\btable stakes\b/i,
82
+ message: '"Table stakes" is a poker idiom — use "the baseline", "minimum requirement", or "expected standard".'
83
+ },
84
+ {
85
+ pattern: /\bdouble down\b/i,
86
+ message: '"Double down" is a gambling idiom — use "commit fully", "intensify", or "increase investment".'
87
+ },
88
+ {
89
+ pattern: /\bup the ante\b/i,
90
+ message: '"Up the ante" is a poker idiom — use "raise expectations" or "increase the stakes".'
91
+ },
92
+ {
93
+ pattern: /\bwild card\b/i,
94
+ message: '"Wild card" is a card game idiom — use "unpredictable factor", "unknown variable", or "surprise".'
95
+ },
96
+ {
97
+ pattern: /\bace up (?:your|their|his|her|one's) sleeve\b/i,
98
+ message: '"Ace up your sleeve" is a card game idiom — use "hidden advantage" or "secret resource".'
99
+ },
100
+ {
101
+ pattern: /\bpoker face\b/i,
102
+ message: '"Poker face" is a gambling idiom — use "neutral expression" or "composure".'
103
+ },
104
+
105
+ // === Military idioms ===
106
+ {
107
+ pattern: /\btop cover\b/i,
108
+ message: '"Top cover" is military jargon — use "organizational cover", "leadership protection", or "executive support".'
109
+ },
110
+ {
111
+ pattern: /\bgive[sn]? (?:\w+ )?ammunition\b/i,
112
+ message: '"Give ammunition" is a military metaphor — use "give evidence", "provide support", or "arm with data".'
113
+ },
114
+ {
115
+ pattern: /\bin the trenches\b/i,
116
+ message: '"In the trenches" is a military idiom — use "doing the daily work" or "on the front lines of the work".'
117
+ },
118
+ {
119
+ pattern: /\brally(?:ing)? the troops\b/i,
120
+ message: '"Rally the troops" is a military idiom — use "motivate the team" or "build team energy".'
121
+ },
122
+ {
123
+ pattern: /\bwar room\b/i,
124
+ message: '"War room" is a military idiom — use "incident channel", "command center", or "situation room".'
125
+ },
126
+
127
+ // === Region-specific idioms ===
128
+ {
129
+ pattern: /\bgame of telephone\b/i,
130
+ message: '"Game of telephone" is a US-specific children\'s game name — describe the concept directly (e.g., "each relay loses fidelity").'
131
+ },
132
+ {
133
+ pattern: /\bpaper(?:ed|ing)? over\b/i,
134
+ message: '"Paper over" is a culturally opaque idiom — use "hide", "conceal", or "cover up".'
135
+ },
136
+ {
137
+ pattern: /\bspreading the gospel\b/i,
138
+ message: '"Spreading the gospel" is a religious idiom — use "campaigning for it", "advocating", or "promoting".'
139
+ },
140
+ {
141
+ pattern: /\bevangelist(?:s)?\b/i,
142
+ message: '"Evangelist" is a religious term — use "advocate", "promoter", or "champion".'
143
+ },
144
+ {
145
+ pattern: /\bdrink(?:ing)? the Kool-Aid\b/i,
146
+ message: '"Drinking the Kool-Aid" is a US-specific reference — use "accepting uncritically" or "adopting without question".'
147
+ },
148
+ {
149
+ pattern: /\bdrink(?:ing)? from (?:a |the )?firehose\b/i,
150
+ message: '"Drinking from a firehose" is a North American idiom — use "overwhelmed with information" or "flooded with data".'
151
+ },
152
+ {
153
+ pattern: /\bboil(?:ing)? the ocean\b/i,
154
+ message: '"Boiling the ocean" is an English idiom — use "taking on too much" or "trying to do everything at once".'
155
+ },
156
+ {
157
+ pattern: /\bzip code\b/i,
158
+ message: '"Zip code" is US-specific — use "postal code" for a global audience.'
159
+ },
160
+ {
161
+ pattern: /\bpass(?:ing|ed)? the buck\b/i,
162
+ message: '"Passing the buck" is an American idiom — use "shifting responsibility" or "deflecting blame".'
163
+ },
164
+ {
165
+ pattern: /\bsweep(?:ing)? (?:it |things? )?under the rug\b/i,
166
+ message: '"Sweep under the rug" is an English idiom — use "hide", "ignore", or "suppress".'
167
+ },
168
+ {
169
+ pattern: /\bread(?:ing)? the room\b/i,
170
+ message: '"Read the room" is an English idiom — use "gauge the mood", "assess the situation", or "pick up on context".'
171
+ },
172
+ {
173
+ pattern: /\bseat at the table\b/i,
174
+ message: '"Seat at the table" is an American business idiom — use "a voice in the conversation", "influence", or "representation".'
175
+ },
176
+ {
177
+ pattern: /\bpull(?:ed|ing)? the plug\b/i,
178
+ message: '"Pull the plug" is an English idiom — use "cancel", "stop", "shut down", or "end".'
179
+ },
180
+ {
181
+ pattern: /\btee[ds]? up\b/i,
182
+ message: '"Tee up" is a golf idiom — use "prepare", "set up", "queue", or "introduce".'
183
+ },
184
+
185
+ // === Domestic/food idioms ===
186
+ {
187
+ pattern: /\bhave (?:your|their|his|her) cake and eat it\b/i,
188
+ message: '"Have your cake and eat it" is an English idiom — use "have both" or "get all the benefits without tradeoffs".'
189
+ },
190
+ {
191
+ pattern: /\bput all (?:your|their|his|her|the) eggs in one basket\b/i,
192
+ message: '"Put all eggs in one basket" is an English idiom — use "concentrate all resources in one place" or "rely on a single approach".'
193
+ },
194
+
195
+ // === Colloquial/slang ===
196
+ {
197
+ pattern: /\bon deck\b/i,
198
+ message: '"On deck" is a baseball/nautical idiom — use "upcoming", "next", or "queued".'
199
+ },
200
+ {
201
+ pattern: /\bhash(?:ed|ing)? (?:\w+ )?out\b/i,
202
+ message: '"Hash it out" is an English idiom — use "resolve", "work through", or "discuss thoroughly".'
203
+ },
204
+ {
205
+ pattern: /\bcrickets\b/i,
206
+ message: '"Crickets" (meaning silence) is a North American idiom — use "silence" or "no response".'
207
+ },
208
+ {
209
+ pattern: /\btake the hint\b/i,
210
+ message: '"Take the hint" is an English idiom — use "recognize the signal" or "understand the implication".'
211
+ },
212
+ {
213
+ pattern: /\bspun (?:their |our |your )?wheels\b/i,
214
+ message: '"Spun wheels" is an English idiom — use "made no progress" or "wasted effort".'
215
+ },
216
+ {
217
+ pattern: /\bget (?:it |a \w+ )?out the door\b/i,
218
+ message: '"Get out the door" is an English idiom — use "ship", "deliver", or "release".'
219
+ },
220
+ {
221
+ pattern: /\bfair game\b/i,
222
+ message: '"Fair game" is an English hunting idiom — use "legitimate topics" or "acceptable targets".'
223
+ },
224
+ {
225
+ pattern: /\bboots on the ground\b/i,
226
+ message: '"Boots on the ground" is a military idiom — use "people doing the actual work" or "on-site staff".'
227
+ },
228
+
229
+ // === Additional baseball idioms ===
230
+ {
231
+ pattern: /\bout of left field\b/i,
232
+ message: '"Out of left field" is a baseball idiom — use "unexpected", "out of nowhere", or "surprising".'
233
+ },
234
+ {
235
+ pattern: /\bswing(?:ing)? for the fences\b/i,
236
+ message: '"Swing for the fences" is a baseball idiom — use "aim high", "go all in", or describe the ambition.'
237
+ },
238
+ {
239
+ pattern: /\bgo(?:es|ing)? to bat for\b/i,
240
+ message: '"Go to bat for" is a baseball idiom — use "advocate for", "support", or "stand up for".'
241
+ },
242
+ {
243
+ pattern: /\binside baseball\b/i,
244
+ message: '"Inside baseball" is a baseball idiom — use "insider detail" or "niche specifics".'
245
+ },
246
+ {
247
+ pattern: /\brain check\b/i,
248
+ message: '"Rain check" is a baseball idiom — use "reschedule", "postpone", or "another time".'
249
+ },
250
+ {
251
+ pattern: /\bplay(?:ing)? hardball\b/i,
252
+ message: '"Play hardball" is a baseball idiom — use "negotiate aggressively" or "take a firm stance".'
253
+ },
254
+
255
+ // === Additional American football idioms ===
256
+ {
257
+ pattern: /\b(?:Monday[- ]morning|armchair) quarterback(?:ing)?\b/i,
258
+ message: '"Monday-morning/armchair quarterback" is an American football idiom — use "second-guess in hindsight" or "criticize after the fact".'
259
+ },
260
+
261
+ // === Basketball idioms ===
262
+ {
263
+ pattern: /\bfull[- ]court press\b/i,
264
+ message: '"Full-court press" is a basketball idiom — use "all-out effort" or "intense, sustained push".'
265
+ },
266
+
267
+ // === Tennis idioms ===
268
+ {
269
+ pattern: /\bball(?:'s| is) in (?:your|their|his|her|our) court\b/i,
270
+ message: '"The ball is in your court" is a tennis idiom — use "it\'s your decision" or "the next step is yours".'
271
+ },
272
+
273
+ // === Horse racing / track idioms ===
274
+ {
275
+ pattern: /\bdown to the wire\b/i,
276
+ message: '"Down to the wire" is a horse-racing idiom — use "until the last moment" or "right up to the deadline".'
277
+ },
278
+ {
279
+ pattern: /\blong shot\b/i,
280
+ message: '"Long shot" is a horse-racing idiom — use "unlikely" or "low-probability".'
281
+ },
282
+ {
283
+ pattern: /\bneck and neck\b/i,
284
+ message: '"Neck and neck" is a horse-racing idiom — use "evenly matched" or "very close".'
285
+ },
286
+ {
287
+ pattern: /\bfront[- ]runner\b/i,
288
+ message: '"Front-runner" is a racing idiom — use "leading candidate" or "the favorite".'
289
+ },
290
+ {
291
+ pattern: /\bjump(?:ed|ing)? the gun\b/i,
292
+ message: '"Jump the gun" is a track-and-field idiom — use "act prematurely" or "start too early".'
293
+ },
294
+
295
+ // === Boxing idioms ===
296
+ {
297
+ pattern: /\bthr(?:ow|ew|owing|own) in the towel\b/i,
298
+ message: '"Throw in the towel" is a boxing idiom — use "give up", "concede", or "stop".'
299
+ },
300
+ {
301
+ pattern: /\broll(?:ing)? with the punches\b/i,
302
+ message: '"Roll with the punches" is a boxing idiom — use "adapt", "stay flexible", or "absorb setbacks".'
303
+ },
304
+ {
305
+ pattern: /\bbelow the belt\b/i,
306
+ message: '"Below the belt" is a boxing idiom — use "unfair" or "out of bounds".'
307
+ },
308
+ {
309
+ pattern: /\bsaved by the bell\b/i,
310
+ message: '"Saved by the bell" is a boxing idiom — use "rescued at the last moment".'
311
+ },
312
+ {
313
+ pattern: /\bthe gloves (?:are|come) off\b/i,
314
+ message: '"The gloves are off" is a boxing idiom — use "no longer holding back" or describe the escalation.'
315
+ },
316
+
317
+ // === Pool/billiards idioms ===
318
+ {
319
+ pattern: /\bbehind the eight ball\b/i,
320
+ message: '"Behind the eight ball" is a billiards idiom — use "at a disadvantage" or "in a tough spot".'
321
+ },
322
+
323
+ // === Bowling idioms ===
324
+ {
325
+ pattern: /\bright up (?:your|their|his|her|my|our) alley\b/i,
326
+ message: '"Right up your alley" is a bowling idiom — use "well suited to you" or "a good fit".'
327
+ },
328
+
329
+ // === American colloquialisms ===
330
+ {
331
+ pattern: /\bkick(?:ed|ing)? the tires\b/i,
332
+ message: '"Kick the tires" is an American car-buying idiom — use "evaluate", "try out", or "test".'
333
+ },
334
+ {
335
+ pattern: /\bkick(?:ed|ing)? the can down the road\b/i,
336
+ message: '"Kick the can down the road" is an American idiom — use "defer", "postpone", or "put off the decision".'
337
+ },
338
+ {
339
+ pattern: /\bthe whole nine yards\b/i,
340
+ message: '"The whole nine yards" is an American idiom — use "everything", "the full set", or "all of it".'
341
+ },
342
+ {
343
+ pattern: /\bunder the bus\b/i,
344
+ message: '"Throw under the bus" is an American idiom — use "blame", "sacrifice", or "betray".'
345
+ },
346
+ {
347
+ pattern: /\bcircl(?:e[sd]?|ing) the wagons\b/i,
348
+ message: '"Circle the wagons" is an American frontier idiom — use "close ranks", "get defensive", or "regroup".'
349
+ },
350
+ {
351
+ pattern: /\bducks in a row\b/i,
352
+ message: '"Ducks in a row" is an American idiom — use "organized", "prepared", or "details sorted".'
353
+ },
354
+ {
355
+ pattern: /\bwheelhouse\b/i,
356
+ message: '"Wheelhouse" is an American (baseball-origin) idiom — use "area of strength" or "specialty".'
357
+ },
358
+
359
+ // === Food/domestic idioms ===
360
+ {
361
+ pattern: /\bpiece of cake\b/i,
362
+ message: '"Piece of cake" is an English idiom — use "easy" or "simple".'
363
+ },
364
+ {
365
+ pattern: /\bbite off more than (?:you|they|he|she|we|I) can chew\b/i,
366
+ message: '"Bite off more than you can chew" is an English idiom — use "take on too much" or "overcommit".'
367
+ },
368
+ {
369
+ pattern: /\bspill(?:ed|ing)? the beans\b/i,
370
+ message: '"Spill the beans" is an English idiom — use "reveal", "disclose", or "give it away".'
371
+ },
372
+ {
373
+ pattern: /\bbring(?:s|ing)? home the bacon\b/i,
374
+ message: '"Bring home the bacon" is an English idiom — use "earn a living" or "deliver results".'
375
+ },
376
+ {
377
+ pattern: /\bproof is in the pudding\b/i,
378
+ message: '"The proof is in the pudding" is a British idiom — use "results prove it" or "judge by outcomes".'
379
+ },
380
+ {
381
+ pattern: /\b(?:not |isn['']t )?(?:my|their|his|her|your) cup of tea\b/i,
382
+ message: '"Not my cup of tea" is a British idiom — use "not my preference" or "not for me".'
383
+ }
384
+ ];
385
+
386
+ /**
387
+ * @param {import("@textlint/types").TextlintRuleContext} context
388
+ * @param {Object} options
389
+ * @param {string[]} [options.allow] - Patterns to skip (by message substring)
390
+ */
391
+ function reporter(context, options = {}) {
392
+ const { Syntax, getSource, report, RuleError } = context;
393
+ const allow = options.allow || [];
394
+
395
+ return {
396
+ [Syntax.Paragraph](node) {
397
+ // Strip inline code spans so quoted examples don't trigger rules
398
+ const text = getSource(node).replace(/`[^`]+`/g, match => ' '.repeat(match.length));
399
+
400
+ for (const { pattern, message } of PATTERNS) {
401
+ if (allow.some(a => message.includes(a))) {
402
+ continue;
403
+ }
404
+
405
+ // Reset regex state for global/multiline patterns
406
+ pattern.lastIndex = 0;
407
+ const match = pattern.exec(text);
408
+
409
+ if (match) {
410
+ const index = match.index;
411
+ report(node, new RuleError(message, { index }));
412
+ }
413
+ }
414
+ }
415
+ };
416
+ }
417
+
418
+ const rule = {
419
+ linter: reporter
420
+ };
421
+
422
+ export default rule;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * textlint rule: no-vague-intensifiers
3
+ *
4
+ * Detects vague intensifiers and superlative stacking, which plain-language
5
+ * style guides recommend replacing with specific, concrete language.
6
+ *
7
+ * Catches:
8
+ * - Superlative stacking ("very unique", "highly effective", "extremely important")
9
+ * - Vague intensifiers before adjectives ("very important", "really critical")
10
+ * - "This ensures/allows/enables" constructions
11
+ */
12
+
13
+ const PATTERNS = [
14
+ // === Superlative stacking ===
15
+ {
16
+ pattern: /\bvery unique\b/i,
17
+ message: '"Very unique" is redundant — "unique" already means one of a kind. Drop "very" or use a more specific word.'
18
+ },
19
+ {
20
+ pattern: /\bhighly effective\b/i,
21
+ message: '"Highly effective" is vague — provide evidence, metrics, or describe the specific effect.'
22
+ },
23
+ {
24
+ pattern: /\bextremely important\b/i,
25
+ message: '"Extremely important" is vague — explain why it matters instead.'
26
+ },
27
+ {
28
+ pattern: /\bincredibly (?:important|valuable|powerful|useful)\b/i,
29
+ message: '"Incredibly [adjective]" is vague — be specific about what makes it so.'
30
+ },
31
+ {
32
+ pattern: /\bvery (?:important|critical|crucial|essential|significant|powerful|valuable)\b/i,
33
+ message: '"Very [adjective]" is a vague intensifier — explain why it matters or provide specifics.'
34
+ },
35
+ {
36
+ pattern: /\breally (?:important|critical|crucial|essential|significant|powerful|valuable|effective)\b/i,
37
+ message: '"Really [adjective]" is a vague intensifier — explain why it matters or provide specifics.'
38
+ },
39
+ {
40
+ pattern: /\btruly (?:important|critical|crucial|essential|significant|powerful|valuable|effective|unique|remarkable|transformative)\b/i,
41
+ message: '"Truly [adjective]" is a vague intensifier — explain why it matters or provide specifics.'
42
+ },
43
+ {
44
+ pattern: /\bsignificantly improved\b/i,
45
+ message: '"Significantly improved" is vague — quantify the improvement or describe the specific change.'
46
+ },
47
+ {
48
+ pattern: /\bmajor impact\b/i,
49
+ message: '"Major impact" is vague — describe the actual effects or provide evidence.'
50
+ },
51
+
52
+ // === "This ensures/allows/enables" constructions ===
53
+ {
54
+ pattern: /(?:^|(?<=\. ))This (?:ensures|allows|enables|guarantees|makes sure) /m,
55
+ message: '"This ensures/allows/enables…" is a formulaic construction — name the subject and use a more direct verb.'
56
+ }
57
+ ];
58
+
59
+ /**
60
+ * @param {import("@textlint/types").TextlintRuleContext} context
61
+ * @param {Object} options
62
+ * @param {string[]} [options.allow] - Patterns to skip (by message substring)
63
+ */
64
+ function reporter(context, options = {}) {
65
+ const { Syntax, getSource, report, RuleError } = context;
66
+ const allow = options.allow || [];
67
+
68
+ return {
69
+ [Syntax.Paragraph](node) {
70
+ const text = getSource(node);
71
+
72
+ for (const { pattern, message } of PATTERNS) {
73
+ if (allow.some(a => message.includes(a))) {
74
+ continue;
75
+ }
76
+
77
+ // Reset regex state for global/multiline patterns
78
+ pattern.lastIndex = 0;
79
+ const match = pattern.exec(text);
80
+
81
+ if (match) {
82
+ const index = match.index;
83
+ report(node, new RuleError(message, { index }));
84
+ }
85
+ }
86
+ }
87
+ };
88
+ }
89
+
90
+ const rule = {
91
+ linter: reporter
92
+ };
93
+
94
+ export default rule;
@@ -0,0 +1,79 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ $-- Adapted from pandoc's default template. Copyright (c) 2014--2024, John MacFarlane; dual-licensed GPL-2.0-or-later or BSD-3-Clause (see NOTICE.md).
3
+ <!DOCTYPE html>
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"$if(lang)$ lang="$lang$" xml:lang="$lang$"$endif$>
5
+ <head>
6
+ <meta charset="utf-8" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <meta name="generator" content="pandoc" />
9
+ <title>$pagetitle$</title>
10
+ <style>
11
+ $if(csl-css)$
12
+ $styles.citations.html()$
13
+ $endif$
14
+ $if(highlighting-css)$
15
+ /* CSS for syntax highlighting */
16
+ $highlighting-css$
17
+ $endif$
18
+ </style>
19
+ $for(css)$
20
+ <link rel="stylesheet" type="text/css" href="$css$" />
21
+ $endfor$
22
+ $for(header-includes)$
23
+ $header-includes$
24
+ $endfor$
25
+ </head>
26
+ <body class="prose"$if(coverpage)$ id="cover"$endif$$if(body-type)$ epub:type="$body-type$"$endif$>
27
+ $if(titlepage)$
28
+ <section epub:type="titlepage" class="titlepage">
29
+ $for(title)$
30
+ $if(title.type)$
31
+ <h1 class="$title.type$">$title.text$</h1>
32
+ $else$
33
+ <h1 class="title">$title$</h1>
34
+ $endif$
35
+ $endfor$
36
+ $if(subtitle)$
37
+ <p class="subtitle">$subtitle$</p>
38
+ $endif$
39
+ $for(author)$
40
+ <p class="author">$author$</p>
41
+ $endfor$
42
+ $for(creator)$
43
+ <p class="$creator.role$">$creator.text$</p>
44
+ $endfor$
45
+ $if(publisher)$
46
+ <p class="publisher">$publisher$</p>
47
+ $endif$
48
+ $if(date)$
49
+ <p class="date">$date$</p>
50
+ $endif$
51
+ $if(rights)$
52
+ <div class="rights">$rights$</div>
53
+ $endif$
54
+ $if(abstract)$
55
+ <div class="abstract">
56
+ <div class="abstract-title">$abstract-title$</div>
57
+ $abstract$
58
+ </div>
59
+ $endif$
60
+ </section>
61
+ $else$
62
+ $if(coverpage)$
63
+ <div id="cover-image" role="img" aria-label="$if(cover-label)$$cover-label$$else$Book cover:$endif$ $pagetitle$">
64
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100%" height="100%" viewBox="0 0 $cover-image-width$ $cover-image-height$" preserveAspectRatio="xMidYMid">
65
+ <image width="$cover-image-width$" height="$cover-image-height$" xlink:href="../media/$cover-image$" />
66
+ </svg>
67
+ </div>
68
+ $else$
69
+ $for(include-before)$
70
+ $include-before$
71
+ $endfor$
72
+ $body$
73
+ $for(include-after)$
74
+ $include-after$
75
+ $endfor$
76
+ $endif$
77
+ $endif$
78
+ </body>
79
+ </html>
@@ -0,0 +1,73 @@
1
+ <!DOCTYPE html>
2
+ $-- Adapted from pandoc's default template. Copyright (c) 2014--2024, John MacFarlane; dual-licensed GPL-2.0-or-later or BSD-3-Clause (see NOTICE.md).
3
+ <html xmlns="http://www.w3.org/1999/xhtml"$if(lang)$ lang="$lang$" xml:lang="$lang$"$endif$$if(dir)$ dir="$dir$"$endif$>
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="generator" content="pandoc" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
8
+ $for(author-meta)$
9
+ <meta name="author" content="$author-meta$" />
10
+ $endfor$
11
+ $if(date-meta)$
12
+ <meta name="dcterms.date" content="$date-meta$" />
13
+ $endif$
14
+ $if(keywords)$
15
+ <meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
16
+ $endif$
17
+ $if(description-meta)$
18
+ <meta name="description" content="$description-meta$" />
19
+ $endif$
20
+ <title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
21
+ $if(styles)$
22
+ <style>
23
+ $styles.html()$
24
+ </style>
25
+ $endif$
26
+ $for(css)$
27
+ <link rel="stylesheet" href="$css$" />
28
+ $endfor$
29
+ $for(header-includes)$
30
+ $header-includes$
31
+ $endfor$
32
+ $if(math)$
33
+ $math$
34
+ $endif$
35
+ </head>
36
+ <body class="prose">
37
+ $for(include-before)$
38
+ $include-before$
39
+ $endfor$
40
+ $if(title)$
41
+ <header id="title-block-header">
42
+ <h1 class="title">$title$</h1>
43
+ $if(subtitle)$
44
+ <p class="subtitle">$subtitle$</p>
45
+ $endif$
46
+ $for(author)$
47
+ <p class="author">$author$</p>
48
+ $endfor$
49
+ $if(date)$
50
+ <p class="date">$date$</p>
51
+ $endif$
52
+ $if(abstract)$
53
+ <div class="abstract">
54
+ <div class="abstract-title">$abstract-title$</div>
55
+ $abstract$
56
+ </div>
57
+ $endif$
58
+ </header>
59
+ $endif$
60
+ $if(toc)$
61
+ <nav id="$idprefix$TOC" role="doc-toc">
62
+ $if(toc-title)$
63
+ <h2 id="$idprefix$toc-title">$toc-title$</h2>
64
+ $endif$
65
+ $table-of-contents$
66
+ </nav>
67
+ $endif$
68
+ $body$
69
+ $for(include-after)$
70
+ $include-after$
71
+ $endfor$
72
+ </body>
73
+ </html>