@faircopy/rules-default 1.15.0 → 1.16.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @faircopy/rules-default
2
2
 
3
- Default ruleset for faircopy. Ships three rules targeting the most common landing-page copy patterns.
3
+ Default ruleset for faircopy. Ships nine rules targeting the most common landing-page copy patterns.
4
4
 
5
5
  ## Install
6
6
 
@@ -12,6 +12,33 @@ npm i @faircopy/rules-default
12
12
 
13
13
  ## Rules
14
14
 
15
+ ### `no-complex-sentences`
16
+
17
+ Flags individual sentences whose Flesch-Kincaid grade level exceeds a target.
18
+
19
+ ```
20
+ warn[no-complex-sentences]: sentence readability is grade 18.5 — simplify to 12 or below
21
+ ```
22
+
23
+ **Options:**
24
+
25
+ ```ts
26
+ {
27
+ maxGradeLevel?: number // Default: 12
28
+ minWords?: number // Minimum words before scoring a sentence. Default: 10
29
+ }
30
+ ```
31
+
32
+ **Config example:**
33
+
34
+ ```ts
35
+ rules: {
36
+ 'no-complex-sentences': ['warn', { maxGradeLevel: 10, minWords: 8 }],
37
+ }
38
+ ```
39
+
40
+ ---
41
+
15
42
  ### `no-em-dash`
16
43
 
17
44
  Bans the em-dash character (`—`, U+2014) in marketing copy.
@@ -65,6 +92,32 @@ rules: {
65
92
 
66
93
  ---
67
94
 
95
+ ### `no-filler-words`
96
+
97
+ Bans filler words that pad out a sentence without adding meaning.
98
+
99
+ ```
100
+ error[no-filler-words]: remove "just" — it's filler
101
+ ```
102
+
103
+ **Options:**
104
+
105
+ ```ts
106
+ {
107
+ words: string[] // Default: ['just']
108
+ }
109
+ ```
110
+
111
+ **Config example:**
112
+
113
+ ```ts
114
+ rules: {
115
+ 'no-filler-words': ['error', { words: ['just', 'basically', 'literally'] }],
116
+ }
117
+ ```
118
+
119
+ ---
120
+
68
121
  ### `no-rhetorical-scaffolding`
69
122
 
70
123
  Bans two formulaic patterns:
@@ -86,3 +139,178 @@ error[no-rhetorical-scaffolding]: avoid "Without X / With X" — drop the setup
86
139
  extraPatterns?: string[] // Additional regex patterns to ban.
87
140
  }
88
141
  ```
142
+
143
+ ---
144
+
145
+ ### `no-non-inclusive-language`
146
+
147
+ Flags non-inclusive terms and suggests neutral alternatives.
148
+
149
+ Default terms include `guys`, `manpower`, `whitelist`, `blacklist`, `master`, `slave`, `crazy`, `insane`, `dumb`, `lame`, `sanity check`, `blind spot`, `grandfathered`, and `mankind`.
150
+
151
+ ```
152
+ error[no-non-inclusive-language]: replace "guys" with a neutral alternative such as "everyone, team, folks"
153
+ ```
154
+
155
+ **Options:**
156
+
157
+ ```ts
158
+ {
159
+ terms?: { term: string; alternatives: string[]; exact?: boolean }[]
160
+ allowedTerms?: string[]
161
+ }
162
+ ```
163
+
164
+ **Config example:**
165
+
166
+ ```ts
167
+ rules: {
168
+ 'no-non-inclusive-language': ['error', {
169
+ terms: [
170
+ { term: 'guys', alternatives: ['everyone', 'team'] },
171
+ { term: 'rockstar', alternatives: ['expert', 'skilled'] },
172
+ ],
173
+ allowedTerms: ['master'],
174
+ }],
175
+ }
176
+ ```
177
+
178
+ Set `exact: true` on a multi-word term to match the whole phrase with word boundaries. This is useful for phrases like `sanity check`, ensuring `sanity checker` is not flagged.
179
+
180
+ ---
181
+
182
+ ### `no-redundant-phrases`
183
+
184
+ Flags wordy redundant phrases and suggests concise replacements.
185
+
186
+ Default phrases include:
187
+
188
+ | Phrase | Suggested replacement |
189
+ |---|---|
190
+ | `in order to` | `to` |
191
+ | `due to the fact that` | `because` |
192
+ | `in spite of the fact that` | `although` |
193
+ | `at this point in time` | `now` |
194
+ | `in the event that` | `if` |
195
+ | `for the purpose of` | `to` |
196
+ | `with regard to` | `about` |
197
+ | `in close proximity to` | `near` |
198
+ | `a large number of` | `many` |
199
+ | `the reason is that` | `because` |
200
+ | `it is important to note that` | *(delete)* |
201
+ | `needless to say` | *(delete)* |
202
+
203
+ ```
204
+ warn[no-redundant-phrases]: "in order to" is redundant — use "to"
205
+ ```
206
+
207
+ **Options:**
208
+
209
+ ```ts
210
+ {
211
+ phrases?: { phrase: string; replacement: string }[]
212
+ }
213
+ ```
214
+
215
+ **Config example:**
216
+
217
+ ```ts
218
+ rules: {
219
+ 'no-redundant-phrases': ['warn', {
220
+ phrases: [
221
+ { phrase: 'in order to', replacement: 'to' },
222
+ { phrase: 'touch base', replacement: 'talk' },
223
+ ],
224
+ }],
225
+ }
226
+ ```
227
+
228
+ Set `replacement` to an empty string to suggest deleting the phrase entirely.
229
+
230
+ ---
231
+
232
+ ### `no-cliches`
233
+
234
+ Flags overused or clichéd phrases and suggests fresher alternatives.
235
+
236
+ Default phrases include:
237
+
238
+ | Phrase | Suggested alternatives |
239
+ |---|---|
240
+ | `world-class` | `top-tier`, `exceptional`, `outstanding` |
241
+ | `best-in-class` | `leading`, `top-performing`, `category-leading` |
242
+ | `cutting-edge` | `advanced`, `modern`, `latest` |
243
+ | `state-of-the-art` | `advanced`, `modern`, `sophisticated` |
244
+ | `game changer` | `breakthrough`, `transformation`, `major advance` |
245
+ | `think outside the box` | `be creative`, `innovate`, `find a new approach` |
246
+ | `at the end of the day` | `ultimately`, `finally`, `in summary` |
247
+ | `low-hanging fruit` | `easy wins`, `quick opportunities`, `simple targets` |
248
+ | `move the needle` | `make a measurable difference`, `drive results`, `create impact` |
249
+ | `circle back` | `follow up`, `reconnect`, `return to this` |
250
+ | `hit the ground running` | `start quickly`, `get started immediately`, `begin effectively` |
251
+ | `boil the ocean` | `take on too much`, `overcomplicate`, `lose focus` |
252
+ | `paradigm shift` | `fundamental change`, `new approach`, `transformation` |
253
+ | `next level` | `advanced`, `improved`, `elevated` |
254
+ | `seamless` | `smooth`, `effortless`, `frictionless` |
255
+ | `robust` | `strong`, `resilient`, `reliable` |
256
+ | `leverage` | `use`, `take advantage of`, `utilize` |
257
+ | `synergy` | `collaboration`, `combined effect`, `partnership` |
258
+
259
+ ```
260
+ warn[no-cliches]: replace "world-class" with a fresher alternative such as "top-tier, exceptional, outstanding"
261
+ ```
262
+
263
+ **Options:**
264
+
265
+ ```ts
266
+ {
267
+ phrases?: { phrase: string; alternatives: string[] }[]
268
+ allow?: string[]
269
+ }
270
+ ```
271
+
272
+ **Config example:**
273
+
274
+ ```ts
275
+ rules: {
276
+ 'no-cliches': ['warn', {
277
+ phrases: [
278
+ { phrase: 'world-class', alternatives: ['top-tier', 'exceptional'] },
279
+ { phrase: 'low-hanging fruit', alternatives: ['easy wins'] },
280
+ ],
281
+ allow: ['robust'],
282
+ }],
283
+ }
284
+ ```
285
+
286
+ ---
287
+
288
+ ### `no-passive-voice`
289
+
290
+ Flags likely passive-voice constructions using auxiliary + past participle patterns.
291
+
292
+ ```
293
+ warn[no-passive-voice]: rewrite passive construction "was approved" with a named actor
294
+ ```
295
+
296
+ Passive voice often hides the actor and adds drag. Prefer naming who did the action unless the actor genuinely does not matter.
297
+
298
+ **Options:**
299
+
300
+ ```ts
301
+ {
302
+ auxiliaries?: string[] // Default: ['is', 'are', 'was', 'were', 'be', 'been', 'being']
303
+ participles?: string[] // Past participles to flag. Default is a curated list of common action participles.
304
+ allowedPhrases?: string[] // Phrases to allow even if they match the passive pattern.
305
+ }
306
+ ```
307
+
308
+ **Config example:**
309
+
310
+ ```ts
311
+ rules: {
312
+ 'no-passive-voice': ['warn', {
313
+ allowedPhrases: ['is licensed', 'was founded'],
314
+ }],
315
+ }
316
+ ```
package/dist/index.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { Rule } from '@faircopy/core';
2
2
 
3
+ interface NoComplexSentencesOptions {
4
+ /** Target Flesch-Kincaid grade level. Sentences scoring above this are flagged. */
5
+ maxGradeLevel?: number;
6
+ /** Minimum words a sentence must contain before it is scored. Shorter sentences are too noisy. */
7
+ minWords?: number;
8
+ }
9
+ declare const noComplexSentences: Rule<NoComplexSentencesOptions>;
10
+
3
11
  interface NoEmDashOptions {
4
12
  /** Additionally flag en-dashes (U+2013). Default false. */
5
13
  flagEnDash?: boolean;
@@ -23,7 +31,67 @@ interface NoRhetoricalScaffoldingOptions {
23
31
  }
24
32
  declare const noRhetoricalScaffolding: Rule<NoRhetoricalScaffoldingOptions>;
25
33
 
34
+ interface NonInclusiveTerm {
35
+ term: string;
36
+ alternatives: string[];
37
+ /** Set to true on a multi-word phrase to require word boundaries. Default false matches the phrase anywhere. Single-word terms always use word boundaries. */
38
+ exact?: boolean;
39
+ }
40
+ interface NoNonInclusiveLanguageOptions {
41
+ /** Terms to flag with suggested alternatives. */
42
+ terms?: NonInclusiveTerm[];
43
+ /** Additional allowed terms that override defaults. */
44
+ allowedTerms?: string[];
45
+ }
46
+ declare const noNonInclusiveLanguage: Rule<NoNonInclusiveLanguageOptions>;
47
+
48
+ interface RedundantPhrase {
49
+ phrase: string;
50
+ replacement: string;
51
+ }
52
+ interface NoRedundantPhrasesOptions {
53
+ phrases?: RedundantPhrase[];
54
+ }
55
+ declare const noRedundantPhrases: Rule<NoRedundantPhrasesOptions>;
56
+
57
+ interface NoPassiveVoiceOptions {
58
+ /** Auxiliary verbs that can introduce a passive construction. */
59
+ auxiliaries?: string[];
60
+ /** Past participles to flag when preceded by an auxiliary. */
61
+ participles?: string[];
62
+ /** Phrases to allow even if they match the passive pattern. */
63
+ allowedPhrases?: string[];
64
+ }
65
+ declare const noPassiveVoice: Rule<NoPassiveVoiceOptions>;
66
+
67
+ interface ClichePhrase {
68
+ phrase: string;
69
+ alternatives: string[];
70
+ }
71
+ interface NoClichesOptions {
72
+ /** Phrases to flag with suggested alternatives. Replaces the default list. */
73
+ phrases?: ClichePhrase[];
74
+ /** Default phrases to allow. */
75
+ allow?: string[];
76
+ }
77
+ declare const noCliches: Rule<NoClichesOptions>;
78
+
79
+ interface NoRepetitiveSentenceStartingsOptions {
80
+ /** Number of consecutive sentences that must share the same starting word before flagging. */
81
+ threshold?: number;
82
+ /** Minimum sentence length (in words) to count. Very short sentences are ignored. */
83
+ minWords?: number;
84
+ /** Words or phrases that are allowed to start multiple sentences. */
85
+ allow?: string[];
86
+ }
87
+ declare const noRepetitiveSentenceStartings: Rule<NoRepetitiveSentenceStartingsOptions>;
88
+
89
+ interface NoFillerWordsOptions {
90
+ words: string[];
91
+ }
92
+ declare const noFillerWords: Rule<NoFillerWordsOptions>;
93
+
26
94
  /** All built-in rules keyed by their rule ID. */
27
95
  declare const ruleRegistry: Map<string, Rule>;
28
96
 
29
- export { type NoEmDashOptions, type NoRhetoricalScaffoldingOptions, type NoWeaselWordsOptions, noEmDash, noRhetoricalScaffolding, noWeaselWords, ruleRegistry };
97
+ export { type ClichePhrase, type NoClichesOptions, type NoComplexSentencesOptions, type NoEmDashOptions, type NoFillerWordsOptions, type NoNonInclusiveLanguageOptions, type NoPassiveVoiceOptions, type NoRedundantPhrasesOptions, type NoRepetitiveSentenceStartingsOptions, type NoRhetoricalScaffoldingOptions, type NoWeaselWordsOptions, type NonInclusiveTerm, type RedundantPhrase, noCliches, noComplexSentences, noEmDash, noFillerWords, noNonInclusiveLanguage, noPassiveVoice, noRedundantPhrases, noRepetitiveSentenceStartings, noRhetoricalScaffolding, noWeaselWords, ruleRegistry };