@faircopy/rules-default 1.18.0 → 1.19.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
@@ -175,7 +175,7 @@ rules: {
175
175
  }
176
176
  ```
177
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.
178
+ Single-word terms always match with word boundaries, so `master` will not flag `masterpiece`. Default multi-word phrases such as `sanity check` and `blind spot` use `exact: true`, so `sanity checker` is not flagged. Set `exact: false` on a custom multi-word term to match it as a substring anywhere in the text.
179
179
 
180
180
  ---
181
181
 
package/dist/index.js CHANGED
@@ -209,8 +209,8 @@ var DEFAULT_TERMS = [
209
209
  { term: "insane", alternatives: ["extreme", "unbelievable", "remarkable"] },
210
210
  { term: "dumb", alternatives: ["unhelpful", "poor", "uninformed"] },
211
211
  { term: "lame", alternatives: ["unimpressive", "inadequate", "weak"] },
212
- { term: "sanity check", alternatives: ["quick check", "confidence check", "verification"] },
213
- { term: "blind spot", alternatives: ["unaware area", "gap", "oversight"] },
212
+ { term: "sanity check", alternatives: ["quick check", "confidence check", "verification"], exact: true },
213
+ { term: "blind spot", alternatives: ["unaware area", "gap", "oversight"], exact: true },
214
214
  { term: "grandfathered", alternatives: ["legacy status", "exempted"] },
215
215
  { term: "mankind", alternatives: ["humanity", "humankind", "people"] }
216
216
  ];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/no-complex-sentences.ts","../src/no-em-dash.ts","../src/no-weasel-words.ts","../src/no-rhetorical-scaffolding.ts","../src/no-non-inclusive-language.ts","../src/no-redundant-phrases.ts","../src/no-passive-voice.ts","../src/no-cliches.ts","../src/no-repetitive-sentence-startings.ts","../src/no-filler-words.ts","../src/index.ts"],"sourcesContent":["import type { Diagnostic, Rule, RuleInput, Suggestion } from '@faircopy/core'\n\nexport interface NoComplexSentencesOptions {\n /** Target Flesch-Kincaid grade level. Sentences scoring above this are flagged. */\n maxGradeLevel?: number\n /** Minimum words a sentence must contain before it is scored. Shorter sentences are too noisy. */\n minWords?: number\n}\n\nconst DEFAULT_OPTIONS: Required<NoComplexSentencesOptions> = {\n maxGradeLevel: 12,\n minWords: 10,\n}\n\nexport const noComplexSentences: Rule<NoComplexSentencesOptions> = {\n id: 'no-complex-sentences',\n description: 'Flag individual sentences whose Flesch-Kincaid grade level exceeds a target',\n defaults: { ...DEFAULT_OPTIONS },\n help: 'Long, syllable-dense sentences are hard to read. Break them into shorter sentences that each make one point.',\n\n check({ text, sourceMap, options }: RuleInput<NoComplexSentencesOptions>): Diagnostic[] {\n const maxGradeLevel = options.maxGradeLevel ?? DEFAULT_OPTIONS.maxGradeLevel\n const minWords = options.minWords ?? DEFAULT_OPTIONS.minWords\n\n const diagnostics: Diagnostic[] = []\n\n for (const { sentence, start, end } of getSentences(text)) {\n const words = getWords(sentence)\n if (words.length < minWords || words.length === 0) continue\n\n const syllables = words.reduce((sum, word) => sum + countSyllables(word), 0)\n const grade = fleschKincaidGrade(words.length, 1, syllables)\n\n if (grade <= maxGradeLevel) continue\n\n const sourceStart = sourceMap[start]\n const sourceEnd = sourceMap[end - 1]\n if (sourceStart === undefined || sourceEnd === undefined) continue\n\n const roundedGrade = Math.round(grade * 10) / 10\n const suggest: Suggestion = {\n description: 'Split this sentence into shorter sentences, one idea each.',\n edits: [],\n }\n\n diagnostics.push({\n ruleId: 'no-complex-sentences',\n severity: 'warn',\n message: `sentence readability is grade ${roundedGrade.toFixed(1)} — simplify to ${maxGradeLevel} or below`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noComplexSentences.help,\n suggest,\n })\n }\n\n return diagnostics\n },\n}\n\nfunction getSentences(text: string): Array<{ sentence: string; start: number; end: number }> {\n const sentences: Array<{ sentence: string; start: number; end: number }> = []\n const abbreviationPattern = /\\b(?:dr|mr|mrs|ms|prof|sr|jr|eg|ie|etc|vs|vol|fig|no)\\.|\\b(?:a|p)\\.m\\./gi\n const placeholder = '\\u0000'\n const masked = text.replace(abbreviationPattern, (match, offset) => {\n // a.m./p.m. may use their trailing period as a sentence terminator. Keep it\n // when followed by whitespace and an uppercase letter or end of string.\n if (/\\b(?:a|p)\\.m\\.$/i.test(match)) {\n const after = text.slice(offset + match.length)\n if (/^\\s+(?:[A-Z]|$)/.test(after)) {\n return match[0] + placeholder + match.slice(2)\n }\n }\n return match.replaceAll('.', placeholder)\n })\n\n const terminator = /[.!?]+/g\n let lastEnd = 0\n let match: RegExpExecArray | null\n\n while ((match = terminator.exec(masked)) !== null) {\n const end = match.index + match[0].length\n const sentence = masked.slice(lastEnd, end).replaceAll(placeholder, '.')\n const trimmed = sentence.trimStart()\n const leadingSpace = sentence.length - trimmed.length\n sentences.push({ sentence: trimmed, start: lastEnd + leadingSpace, end })\n lastEnd = end\n }\n\n return sentences\n}\n\nfunction getWords(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9\\s'-]/g, ' ')\n .split(/\\s+/)\n .filter(word => word.length > 0 && /[a-z0-9]/.test(word))\n}\n\nfunction countSyllables(word: string): number {\n const cleaned = word.toLowerCase().replace(/[^a-z]/g, '')\n if (!cleaned) return 0\n if (cleaned.length <= 3) return 1\n\n const vowels = cleaned.match(/[aeiouy]+/g)\n if (!vowels) return 1\n\n let count = vowels.length\n if (cleaned.endsWith('e')) count--\n if (cleaned.endsWith('le') && cleaned.length > 2 && !/[aeiouy]$/.test(cleaned[cleaned.length - 3] ?? '')) {\n count++\n }\n return Math.max(1, count)\n}\n\nfunction fleschKincaidGrade(words: number, sentences: number, syllables: number): number {\n if (sentences === 0 || words === 0) return 0\n return 0.39 * (words / sentences) + 11.8 * (syllables / words) - 15.59\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoEmDashOptions {\n /** Additionally flag en-dashes (U+2013). Default false. */\n flagEnDash?: boolean\n /** Additionally flag ASCII double-hyphen --. Default false. */\n flagDoubleHyphen?: boolean\n}\n\nexport const noEmDash: Rule<NoEmDashOptions> = {\n id: 'no-em-dash',\n description: 'Ban the em-dash character in marketing copy',\n defaults: { flagEnDash: false, flagDoubleHyphen: false },\n help: 'Em-dashes are a stylistic tell. Split the sentence at the break. ' +\n 'Use a period, a semicolon, parentheses, or a new sentence. ' +\n 'If the clauses genuinely belong together and a comma reads worse, write shorter sentences.',\n\n check({ text, sourceMap, options }: RuleInput<NoEmDashOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const opts = { ...noEmDash.defaults, ...options }\n\n const flag = (re: RegExp, message: string) => {\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({ ruleId: 'no-em-dash', severity: 'error', message, range: { start, end }, help: noEmDash.help })\n }\n }\n\n flag(/—/g, 'use a sentence break instead of an em-dash')\n if (opts.flagEnDash) flag(/–/g, 'use a hyphen instead of an en-dash')\n if (opts.flagDoubleHyphen) flag(/--/g, 'use a sentence break instead of --')\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoWeaselWordsOptions {\n words: string[]\n}\n\nconst DEFAULT_WORDS = ['actually', 'truly', 'really', 'literally']\n\nexport const noWeaselWords: Rule<NoWeaselWordsOptions> = {\n id: 'no-weasel-words',\n description: 'Ban reinforcement adverbs that protest too much',\n defaults: { words: DEFAULT_WORDS },\n help: 'Reinforcement adverbs defend a claim instead of making it. ' +\n 'Delete the word. If the sentence no longer reads right, ' +\n 'the original claim was the problem — rewrite it, don\\'t prop it up.',\n\n check({ text, sourceMap, options }: RuleInput<NoWeaselWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const words = options.words?.length ? options.words : DEFAULT_WORDS\n\n for (const word of words) {\n const re = new RegExp(`\\\\b${word}\\\\b`, 'gi')\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-weasel-words',\n severity: 'error',\n message: `remove \"${m[0].toLowerCase()}\" — it weakens the claim`,\n range: { start, end },\n help: noWeaselWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoRhetoricalScaffoldingOptions {\n /** Disable \"X is Y, not Z\" detection. Default false. */\n allowIsNotConstruction?: boolean\n /** Disable \"Without X / With X\" detection. Default false. */\n allowWithoutWithConstruction?: boolean\n /** Additional banned patterns as regex strings. */\n extraPatterns?: string[]\n}\n\n// \"X is Y, not a/an/the/just/only/merely/simply...\"\nconst IS_NOT_RE = /\\b(is|are|was|were)\\s+[^.!?]{1,80},\\s+not\\s+(a|an|the|just|only|merely|simply)\\b/gi\n\n// \"Without ... [sentences] ... With ...\"\nconst WITHOUT_WITH_RE = /\\bWithout\\b[^.!?]{1,200}[.!?]\\s*(?:[^.!?]{1,200}[.!?]\\s*){0,2}With\\b/gs\n\nexport const noRhetoricalScaffolding: Rule<NoRhetoricalScaffoldingOptions> = {\n id: 'no-rhetorical-scaffolding',\n description: 'Ban formulaic \"X is Y, not Z\" and \"Without X / With X\" patterns',\n defaults: { allowIsNotConstruction: false, allowWithoutWithConstruction: false, extraPatterns: [] },\n help: 'These patterns spend a clause denying a straw man or performing a reveal instead of making a claim. ' +\n 'Delete the setup and keep the claim.',\n\n check({ text, sourceMap, options }: RuleInput<NoRhetoricalScaffoldingOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const opts = { ...noRhetoricalScaffolding.defaults, ...options }\n\n if (!opts.allowIsNotConstruction) {\n const re = new RegExp(IS_NOT_RE.source, IS_NOT_RE.flags)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-rhetorical-scaffolding',\n severity: 'error',\n message: 'avoid \"X is Y, not Z\" — state the claim directly',\n range: { start, end },\n help: noRhetoricalScaffolding.help,\n })\n }\n }\n\n if (!opts.allowWithoutWithConstruction) {\n const re = new RegExp(WITHOUT_WITH_RE.source, WITHOUT_WITH_RE.flags)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-rhetorical-scaffolding',\n severity: 'error',\n message: 'avoid \"Without X / With X\" — drop the setup and make the claim',\n range: { start, end },\n help: noRhetoricalScaffolding.help,\n })\n }\n }\n\n for (const pattern of opts.extraPatterns ?? []) {\n const re = new RegExp(pattern, 'gi')\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-rhetorical-scaffolding',\n severity: 'error',\n message: 'banned rhetorical pattern',\n range: { start, end },\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NonInclusiveTerm {\n term: string\n alternatives: string[]\n /** 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. */\n exact?: boolean\n}\n\nexport interface NoNonInclusiveLanguageOptions {\n /** Terms to flag with suggested alternatives. */\n terms?: NonInclusiveTerm[]\n /** Additional allowed terms that override defaults. */\n allowedTerms?: string[]\n}\n\nconst DEFAULT_TERMS: NonInclusiveTerm[] = [\n { term: 'guys', alternatives: ['everyone', 'team', 'folks'] },\n { term: 'manpower', alternatives: ['workforce', 'staffing', 'personnel'] },\n { term: 'whitelist', alternatives: ['allowlist'] },\n { term: 'blacklist', alternatives: ['denylist', 'blocklist'] },\n { term: 'master', alternatives: ['primary', 'main', 'leader'] },\n { term: 'slave', alternatives: ['secondary', 'replica', 'follower'] },\n { term: 'crazy', alternatives: ['unexpected', 'intense', 'extreme'] },\n { term: 'insane', alternatives: ['extreme', 'unbelievable', 'remarkable'] },\n { term: 'dumb', alternatives: ['unhelpful', 'poor', 'uninformed'] },\n { term: 'lame', alternatives: ['unimpressive', 'inadequate', 'weak'] },\n { term: 'sanity check', alternatives: ['quick check', 'confidence check', 'verification'] },\n { term: 'blind spot', alternatives: ['unaware area', 'gap', 'oversight'] },\n { term: 'grandfathered', alternatives: ['legacy status', 'exempted'] },\n { term: 'mankind', alternatives: ['humanity', 'humankind', 'people'] },\n]\n\nfunction escapeRegex(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPattern(term: string, exact: boolean): RegExp {\n const escaped = escapeRegex(term)\n const isPhrase = /\\s/.test(term)\n if (isPhrase) {\n if (exact) {\n return new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n }\n return new RegExp(escaped, 'gi')\n }\n return new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n}\n\nexport const noNonInclusiveLanguage: Rule<NoNonInclusiveLanguageOptions> = {\n id: 'no-non-inclusive-language',\n description: 'Flag non-inclusive terms and suggest neutral alternatives',\n defaults: { terms: DEFAULT_TERMS, allowedTerms: [] },\n help: 'Non-inclusive terms can alienate readers. Replace them with neutral alternatives that name the same idea without relying on identity, ability, or historical power metaphors.',\n\n check({ text, sourceMap, options }: RuleInput<NoNonInclusiveLanguageOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const terms = options.terms?.length ? options.terms : DEFAULT_TERMS\n const allowed = new Set((options.allowedTerms ?? []).map(term => term.toLowerCase()))\n\n for (const { term, alternatives, exact } of terms) {\n if (allowed.has(term.toLowerCase())) continue\n\n const re = buildPattern(term, exact ?? false)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n const suggestion = alternatives.join(', ')\n diagnostics.push({\n ruleId: 'no-non-inclusive-language',\n severity: 'error',\n message: `replace \"${m[0]}\" with a neutral alternative such as \"${suggestion}\"`,\n range: { start, end },\n help: noNonInclusiveLanguage.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput, Suggestion } from '@faircopy/core'\n\nexport interface RedundantPhrase {\n phrase: string\n replacement: string\n}\n\nexport interface NoRedundantPhrasesOptions {\n phrases?: RedundantPhrase[]\n}\n\nconst DEFAULT_PHRASES: RedundantPhrase[] = [\n { phrase: 'in order to', replacement: 'to' },\n { phrase: 'due to the fact that', replacement: 'because' },\n { phrase: 'in spite of the fact that', replacement: 'although' },\n { phrase: 'at this point in time', replacement: 'now' },\n { phrase: 'in the event that', replacement: 'if' },\n { phrase: 'for the purpose of', replacement: 'to' },\n { phrase: 'with regard to', replacement: 'about' },\n { phrase: 'in close proximity to', replacement: 'near' },\n { phrase: 'a large number of', replacement: 'many' },\n { phrase: 'the reason is that', replacement: 'because' },\n { phrase: 'in the vicinity of', replacement: 'near' },\n { phrase: 'on the occasion of', replacement: 'when' },\n { phrase: 'in view of the fact that', replacement: 'because' },\n { phrase: 'owing to the fact that', replacement: 'because' },\n { phrase: 'for the reason that', replacement: 'because' },\n { phrase: 'in light of the fact that', replacement: 'because' },\n { phrase: 'it is important to note that', replacement: '' },\n { phrase: 'it should be noted that', replacement: '' },\n { phrase: 'needless to say', replacement: '' },\n { phrase: 'it goes without saying that', replacement: '' },\n]\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPhrasePattern(phrase: string): RegExp {\n const escaped = escapeRegExp(phrase).replace(/\\\\s+/g, '\\\\s+')\n return new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n}\n\nexport const noRedundantPhrases: Rule<NoRedundantPhrasesOptions> = {\n id: 'no-redundant-phrases',\n description: 'Flag wordy redundant phrases and suggest concise replacements',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Redundant phrases pad copy with extra words that add no meaning. ' +\n 'Replace them with the concise alternative, or delete the phrase entirely ' +\n 'if the replacement is empty.',\n\n check({ text, sourceMap, options }: RuleInput<NoRedundantPhrasesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n\n for (const { phrase, replacement } of phrases) {\n const re = buildPhrasePattern(phrase)\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedPhrase = match[0]\n const start = sourceMap[match.index]!\n const end = sourceMap[match.index + matchedPhrase.length - 1]! + 1\n\n const suggest: Suggestion = {\n description: replacement\n ? `replace \"${matchedPhrase}\" with \"${replacement}\"`\n : `delete \"${matchedPhrase}\"`,\n edits: [{ range: { start, end }, replacement }],\n }\n\n diagnostics.push({\n ruleId: 'no-redundant-phrases',\n severity: 'warn',\n message: replacement\n ? `\"${matchedPhrase}\" is redundant — use \"${replacement}\"`\n : `\"${matchedPhrase}\" is redundant — delete it`,\n range: { start, end },\n help: noRedundantPhrases.help,\n suggest,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoPassiveVoiceOptions {\n /** Auxiliary verbs that can introduce a passive construction. */\n auxiliaries?: string[]\n /** Past participles to flag when preceded by an auxiliary. */\n participles?: string[]\n /** Phrases to allow even if they match the passive pattern. */\n allowedPhrases?: string[]\n}\n\nconst DEFAULT_AUXILIARIES = ['is', 'are', 'was', 'were', 'be', 'been', 'being']\n\nconst DEFAULT_PARTICIPLES = [\n 'accepted', 'accomplished', 'achieved', 'acquired', 'added', 'addressed', 'adjusted', 'admired',\n 'admitted', 'adopted', 'advanced', 'affected', 'afforded', 'agreed', 'allowed', 'announced',\n 'answered', 'anticipated', 'approved', 'arranged', 'asked', 'assembled', 'assessed', 'assigned',\n 'assisted', 'assumed', 'assured', 'attached', 'attacked', 'attempted', 'attended', 'attracted',\n 'avoided', 'awarded', 'based', 'beaten', 'become', 'begun', 'believed', 'belonged', 'benefited',\n 'betrayed', 'blamed', 'blessed', 'blocked', 'blown', 'boarded', 'boiled', 'booked', 'borrowed',\n 'bothered', 'bought', 'bound', 'branded', 'broken', 'brought', 'built', 'burned', 'burst',\n 'called', 'captured', 'carried', 'caused', 'caught', 'celebrated', 'challenged', 'changed',\n 'charged', 'chased', 'checked', 'chosen', 'claimed', 'cleaned', 'cleared', 'clicked', 'climbed',\n 'closed', 'coached', 'collected', 'combined', 'come', 'comforted', 'committed', 'communicated',\n 'compared', 'competed', 'completed', 'complicated', 'composed', 'computed', 'conceived',\n 'concentrated', 'concerned', 'concluded', 'conditioned', 'conducted', 'confirmed', 'connected',\n 'considered', 'consisted', 'constructed', 'consulted', 'consumed', 'contacted', 'contained',\n 'continued', 'contributed', 'controlled', 'converted', 'convinced', 'cooked', 'cost', 'counted',\n 'covered', 'created', 'crossed', 'crowded', 'crushed', 'cried', 'cut', 'damaged', 'danced',\n 'dated', 'dealt', 'decided', 'declared', 'declined', 'decorated', 'decreased', 'defeated',\n 'defended', 'defined', 'delayed', 'delivered', 'demanded', 'demonstrated', 'denied', 'departed',\n 'depended', 'described', 'deserved', 'designed', 'destroyed', 'detailed', 'detected', 'determined',\n 'developed', 'devoted', 'differed', 'digested', 'diminished', 'directed', 'discovered', 'discussed',\n 'displayed', 'distributed', 'disturbed', 'divided', 'done', 'doubled', 'doubted', 'drafted',\n 'dragged', 'drawn', 'dressed', 'driven', 'dropped', 'drowned', 'dug', 'earned', 'eaten',\n 'edited', 'educated', 'elected', 'eliminated', 'embarrassed', 'emerged', 'employed', 'enabled',\n 'encouraged', 'ended', 'engaged', 'engineered', 'enjoyed', 'entered', 'entertained', 'equipped',\n 'escaped', 'established', 'estimated', 'evaluated', 'evolved', 'examined', 'exceeded', 'exchanged',\n 'excited', 'excused', 'executed', 'exercised', 'exhausted', 'exhibited', 'expanded', 'expected',\n 'experienced', 'explained', 'exploded', 'explored', 'exported', 'exposed', 'expressed', 'extended',\n 'faced', 'failed', 'fallen', 'favored', 'feared', 'featured', 'fed', 'felt', 'fetched',\n 'fielded', 'filled', 'filmed', 'filtered', 'financed', 'finished', 'fired', 'fitted', 'fixed',\n 'flashed', 'flown', 'focused', 'folded', 'followed', 'forced', 'forgotten', 'formed', 'founded',\n 'framed', 'freed', 'frozen', 'frustrated', 'fueled', 'fulfilled', 'functioned', 'funded',\n 'gained', 'gathered', 'given', 'gone', 'governed', 'grabbed', 'graded', 'granted', 'greeted',\n 'grown', 'guaranteed', 'guarded', 'guessed', 'guided', 'handled', 'hanged', 'happened', 'harmed',\n 'harvested', 'hated', 'headed', 'healed', 'heard', 'heated', 'helped', 'hidden', 'highlighted',\n 'hired', 'hit', 'held', 'honored', 'hooked', 'hoped', 'hosted', 'hunted', 'hurried', 'hurt',\n 'identified', 'ignored', 'illustrated', 'imagined', 'implemented', 'implied', 'imported',\n 'imposed', 'impressed', 'improved', 'included', 'increased', 'indicated', 'influenced', 'informed',\n 'initiated', 'injured', 'inquired', 'inserted', 'inspected', 'inspired', 'installed', 'instructed',\n 'intended', 'interacted', 'interested', 'interrupted', 'interviewed', 'introduced', 'invented',\n 'invested', 'investigated', 'invited', 'involved', 'isolated', 'issued', 'joined', 'judged',\n 'jumped', 'justified', 'kept', 'kicked', 'killed', 'kissed', 'knocked', 'known', 'labeled',\n 'lacked', 'landed', 'lasted', 'launched', 'learned', 'leased', 'left', 'lent', 'let', 'licensed',\n 'lifted', 'lighted', 'liked', 'limited', 'linked', 'listed', 'listened', 'lived', 'loaded',\n 'located', 'locked', 'logged', 'looked', 'lost', 'loved', 'made', 'maintained', 'managed',\n 'manufactured', 'marked', 'marketed', 'married', 'mastered', 'matched', 'mattered', 'matured',\n 'meant', 'measured', 'met', 'mentioned', 'merged', 'messed', 'migrated', 'minded', 'missed',\n 'mixed', 'modified', 'monitored', 'moved', 'multiplied', 'named', 'narrowed', 'needed',\n 'negotiated', 'noted', 'noticed', 'obtained', 'occurred', 'offered', 'opened', 'operated',\n 'opposed', 'ordered', 'organized', 'oriented', 'originated', 'overcome', 'overlooked', 'owned',\n 'paced', 'packed', 'paid', 'painted', 'paired', 'parked', 'participated', 'passed', 'patented',\n 'paused', 'perceived', 'performed', 'permitted', 'persuaded', 'phased', 'picked', 'pictured',\n 'placed', 'planned', 'planted', 'played', 'pleased', 'plugged', 'pointed', 'polished', 'popped',\n 'possessed', 'posted', 'poured', 'powered', 'praised', 'prayed', 'preached', 'preceded',\n 'predicted', 'preferred', 'prepared', 'prescribed', 'presented', 'preserved', 'pressed', 'pretended',\n 'prevented', 'priced', 'printed', 'prioritized', 'processed', 'produced', 'profited', 'programmed',\n 'prohibited', 'promised', 'promoted', 'prompted', 'proposed', 'protected', 'proved', 'provided',\n 'published', 'pulled', 'pumped', 'punched', 'purchased', 'pursued', 'pushed', 'put', 'qualified',\n 'questioned', 'quit', 'quoted', 'raised', 'ranked', 'rated', 'reached', 'reacted', 'read',\n 'realized', 'received', 'recognized', 'recommended', 'reconciled', 'recorded', 'recovered',\n 'recruited', 'reduced', 'referred', 'reflected', 'refused', 'regarded', 'regulated', 'rejected',\n 'related', 'released', 'remained', 'remembered', 'reminded', 'removed', 'rendered', 'renewed',\n 'rented', 'repaired', 'repeated', 'replaced', 'replied', 'reported', 'represented', 'reproduced',\n 'requested', 'required', 'researched', 'reserved', 'resolved', 'respected', 'responded', 'restored',\n 'resulted', 'retained', 'retired', 'retrieved', 'returned', 'revealed', 'reviewed', 'revised',\n 'revived', 'rewarded', 'ridden', 'risen', 'rolled', 'rooted', 'rounded', 'ruled', 'run', 'rushed',\n 'sacrificed', 'said', 'sold', 'sampled', 'saved', 'scanned', 'scared', 'scheduled', 'scored',\n 'scraped', 'scratched', 'screened', 'searched', 'seasoned', 'seated', 'secured', 'seen', 'selected',\n 'sent', 'separated', 'served', 'serviced', 'set', 'settled', 'settled', 'shaped', 'shared',\n 'shocked', 'shaken', 'shaped', 'shipped', 'shocked', 'shot', 'shown', 'shut', 'signed', 'simplified',\n 'singled', 'sited', 'situated', 'sized', 'sketched', 'skilled', 'slammed', 'slashed', 'slid',\n 'slipped', 'slowed', 'smashed', 'smelled', 'smiled', 'smoked', 'snapped', 'soaked', 'sold',\n 'solved', 'sorted', 'sought', 'sounded', 'spared', 'sparked', 'spawned', 'spearheaded', 'specified',\n 'spent', 'spilled', 'spun', 'split', 'spoken', 'sponsored', 'spotted', 'spread', 'sprung',\n 'staged', 'stained', 'staked', 'stalled', 'stamped', 'started', 'stated', 'stationed', 'stayed',\n 'stolen', 'stepped', 'sticked', 'stimulated', 'stirred', 'stopped', 'stored', 'strained',\n 'streamed', 'strengthened', 'stressed', 'stretched', 'stricken', 'struck', 'structured',\n 'struggled', 'studied', 'stuffed', 'styled', 'submitted', 'substituted', 'succeeded', 'sucked',\n 'sued', 'suffered', 'suggested', 'suited', 'summed', 'supplied', 'supported', 'supposed',\n 'surprised', 'surrounded', 'surveyed', 'survived', 'suspected', 'suspended', 'sustained', 'swallowed',\n 'swapped', 'swept', 'swelled', 'swung', 'switched', 'tackled', 'tagged', 'taken', 'talked',\n 'tapped', 'targeted', 'tasted', 'taught', 'torn', 'tested', 'testified', 'texted', 'thanked',\n 'thrown', 'thrust', 'ticked', 'tied', 'tightened', 'timed', 'tipped', 'tired', 'titled',\n 'tolerated', 'topped', 'touched', 'toured', 'tracked', 'traded', 'trained', 'transferred',\n 'transformed', 'translated', 'transmitted', 'transported', 'trapped', 'traveled', 'treated',\n 'trimmed', 'tripled', 'triumphed', 'troubled', 'trusted', 'tried', 'turned', 'twisted', 'typed',\n 'undergone', 'understood', 'undertaken', 'unfolded', 'unified', 'united', 'updated', 'upgraded',\n 'upheld', 'upset', 'used', 'utilized', 'valued', 'vanished', 'varied', 'verified', 'vetoed',\n 'viewed', 'visited', 'voiced', 'voted', 'waged', 'waited', 'walked', 'wandered', 'wanted',\n 'warned', 'warranted', 'washed', 'wasted', 'watched', 'weakened', 'worn', 'welcomed', 'won',\n 'wondered', 'worked', 'worried', 'worshiped', 'wounded', 'written', 'wrung', 'yielded',\n]\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPassivePattern(auxiliaries: string[], participles: string[]): RegExp {\n const auxPattern = auxiliaries.map(escapeRegExp).join('|')\n const participlePattern = participles.map(escapeRegExp).join('|')\n return new RegExp(`\\\\b(${auxPattern})\\\\s+(\\\\w+\\\\s+){0,3}(${participlePattern})\\\\b`, 'gi')\n}\n\nexport const noPassiveVoice: Rule<NoPassiveVoiceOptions> = {\n id: 'no-passive-voice',\n description: 'Flag likely passive-voice constructions using auxiliary + past participle patterns',\n defaults: {\n auxiliaries: DEFAULT_AUXILIARIES,\n participles: DEFAULT_PARTICIPLES,\n allowedPhrases: [],\n },\n help: 'Passive voice often hides the actor and adds drag. ' +\n 'Prefer naming who did the action unless the actor genuinely does not matter.',\n\n check({ text, sourceMap, options }: RuleInput<NoPassiveVoiceOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const auxiliaries = options.auxiliaries?.length ? options.auxiliaries : DEFAULT_AUXILIARIES\n const participles = options.participles?.length ? options.participles : DEFAULT_PARTICIPLES\n const allowed = new Set((options.allowedPhrases ?? []).map(phrase => phrase.toLowerCase()))\n\n const re = buildPassivePattern(auxiliaries, participles)\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedText = match[0]\n const lowerMatch = matchedText.toLowerCase()\n\n let allowedMatch = false\n for (const phrase of allowed) {\n if (lowerMatch.includes(phrase.toLowerCase())) {\n allowedMatch = true\n break\n }\n }\n if (allowedMatch) continue\n\n const start = sourceMap[match.index]!\n const end = sourceMap[match.index + matchedText.length - 1]! + 1\n\n diagnostics.push({\n ruleId: 'no-passive-voice',\n severity: 'warn',\n message: `rewrite passive construction \"${matchedText}\" with a named actor`,\n range: { start, end },\n help: noPassiveVoice.help,\n })\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface ClichePhrase {\n phrase: string\n alternatives: string[]\n}\n\nexport interface NoClichesOptions {\n /** Phrases to flag with suggested alternatives. Replaces the default list. */\n phrases?: ClichePhrase[]\n /** Default phrases to allow. */\n allow?: string[]\n}\n\nconst DEFAULT_PHRASES: ClichePhrase[] = [\n { phrase: 'world-class', alternatives: ['top-tier', 'exceptional', 'outstanding'] },\n { phrase: 'best-in-class', alternatives: ['leading', 'top-performing', 'category-leading'] },\n { phrase: 'cutting-edge', alternatives: ['advanced', 'modern', 'latest'] },\n { phrase: 'state-of-the-art', alternatives: ['advanced', 'modern', 'sophisticated'] },\n { phrase: 'game changer', alternatives: ['breakthrough', 'transformation', 'major advance'] },\n { phrase: 'game-changing', alternatives: ['transformative', 'breakthrough', 'revolutionary'] },\n { phrase: 'think outside the box', alternatives: ['be creative', 'innovate', 'find a new approach'] },\n { phrase: 'at the end of the day', alternatives: ['ultimately', 'finally', 'in summary'] },\n { phrase: 'low-hanging fruit', alternatives: ['easy wins', 'quick opportunities', 'simple targets'] },\n { phrase: 'move the needle', alternatives: ['make a measurable difference', 'drive results', 'create impact'] },\n { phrase: 'circle back', alternatives: ['follow up', 'reconnect', 'return to this'] },\n { phrase: 'give 110%', alternatives: ['do your best', 'make a full effort', 'go all in'] },\n { phrase: 'hit the ground running', alternatives: ['start quickly', 'get started immediately', 'begin effectively'] },\n { phrase: 'boil the ocean', alternatives: ['take on too much', 'overcomplicate', 'lose focus'] },\n { phrase: 'paradigm shift', alternatives: ['fundamental change', 'new approach', 'transformation'] },\n { phrase: 'next level', alternatives: ['advanced', 'improved', 'elevated'] },\n { phrase: 'seamless', alternatives: ['smooth', 'effortless', 'frictionless'] },\n { phrase: 'robust', alternatives: ['strong', 'resilient', 'reliable'] },\n { phrase: 'leverage', alternatives: ['use', 'take advantage of', 'utilize'] },\n { phrase: 'synergy', alternatives: ['collaboration', 'combined effect', 'partnership'] },\n]\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPattern(phrase: string): RegExp {\n const escaped = escapeRegex(phrase)\n return new RegExp(`(?<!\\\\w)${escaped}(?!\\\\w)`, 'gi')\n}\n\nexport const noCliches: Rule<NoClichesOptions> = {\n id: 'no-cliches',\n description: 'Flag overused or clichéd phrases and suggest fresher alternatives',\n defaults: { phrases: DEFAULT_PHRASES, allow: [] },\n help: 'Clichés and overused phrases make copy feel generic and forgettable. ' +\n 'Replace them with specific, concrete language that reflects your actual product or idea.',\n\n check({ text, sourceMap, options }: RuleInput<NoClichesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n const allowed = new Set((options.allow ?? []).map(phrase => phrase.toLowerCase()))\n\n for (const { phrase, alternatives } of phrases) {\n if (allowed.has(phrase.toLowerCase())) continue\n\n const re = buildPattern(phrase)\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedPhrase = match[0]\n const start = sourceMap[match.index]!\n const end = sourceMap[match.index + matchedPhrase.length - 1]! + 1\n const suggestion = alternatives.join(', ')\n\n diagnostics.push({\n ruleId: 'no-cliches',\n severity: 'warn',\n message: `replace \"${matchedPhrase}\" with a fresher alternative such as \"${suggestion}\"`,\n range: { start, end },\n help: noCliches.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoRepetitiveSentenceStartingsOptions {\n /** Number of consecutive sentences that must share the same starting word before flagging. */\n threshold?: number\n /** Minimum sentence length (in words) to count. Very short sentences are ignored. */\n minWords?: number\n /** Words or phrases that are allowed to start multiple sentences. */\n allow?: string[]\n}\n\nconst DEFAULT_OPTIONS: Required<NoRepetitiveSentenceStartingsOptions> = {\n threshold: 3,\n minWords: 3,\n allow: ['the', 'a', 'an', 'it', 'this', 'that'],\n}\n\nfunction getSentences(text: string): Array<{ sentence: string; start: number; end: number }> {\n const sentences: Array<{ sentence: string; start: number; end: number }> = []\n const abbreviationPattern = /\\b(?:dr|mr|mrs|ms|prof|sr|jr|eg|ie|etc|vs|vol|fig|no)\\.|\\b(?:a|p)\\.m\\./gi\n const placeholder = '\\u0000'\n const masked = text.replace(abbreviationPattern, (match, offset) => {\n if (/\\b(?:a|p)\\.m\\.$/i.test(match)) {\n const after = text.slice(offset + match.length)\n if (/^\\s+(?:[A-Z]|$)/.test(after)) {\n return match[0] + placeholder + match.slice(2)\n }\n }\n return match.replaceAll('.', placeholder)\n })\n\n const terminator = /[.!?]+/g\n let lastEnd = 0\n let match: RegExpExecArray | null\n\n while ((match = terminator.exec(masked)) !== null) {\n const end = match.index + match[0].length\n const sentence = masked.slice(lastEnd, end).replaceAll(placeholder, '.')\n const trimmed = sentence.trimStart()\n const leadingSpace = sentence.length - trimmed.length\n sentences.push({ sentence: trimmed, start: lastEnd + leadingSpace, end })\n lastEnd = end\n }\n\n const trailing = masked.slice(lastEnd).trim()\n if (trailing) {\n sentences.push({ sentence: trailing, start: lastEnd, end: text.length })\n }\n\n return sentences\n}\n\nfunction getFirstWord(sentence: string): string | null {\n const match = sentence.trim().match(/^[a-zA-Z0-9]+/)\n return match ? match[0].toLowerCase() : null\n}\n\nfunction countWords(sentence: string): number {\n return sentence\n .replace(/[^a-zA-Z0-9\\s'-]/g, ' ')\n .split(/\\s+/)\n .filter(word => word.length > 0 && /[a-zA-Z0-9]/.test(word))\n .length\n}\n\nexport const noRepetitiveSentenceStartings: Rule<NoRepetitiveSentenceStartingsOptions> = {\n id: 'no-repetitive-sentence-startings',\n description: 'Flag consecutive sentences that start with the same word',\n defaults: { ...DEFAULT_OPTIONS },\n help: 'Starting several consecutive sentences with the same word creates a repetitive rhythm. ' +\n 'Vary the sentence openings or combine related sentences to keep the reader engaged.',\n\n check({ text, sourceMap, options }: RuleInput<NoRepetitiveSentenceStartingsOptions>): Diagnostic[] {\n const threshold = options.threshold ?? DEFAULT_OPTIONS.threshold\n const minWords = options.minWords ?? DEFAULT_OPTIONS.minWords\n const allowed = new Set((options.allow ?? DEFAULT_OPTIONS.allow).map(word => word.toLowerCase()))\n\n const diagnostics: Diagnostic[] = []\n const sentences = getSentences(text)\n\n let runStart = 0\n let runWord: string | null = null\n let runLength = 0\n\n for (let index = 0; index < sentences.length; index++) {\n const { sentence, start, end } = sentences[index]!\n const firstWord = getFirstWord(sentence)\n const words = countWords(sentence)\n\n if (!firstWord || words < minWords || allowed.has(firstWord)) {\n if (runLength >= threshold && runWord) {\n const first = sentences[runStart]!\n const last = sentences[index - 1]!\n const sourceStart = sourceMap[first.start]\n const sourceEnd = sourceMap[last.end - 1]\n if (sourceStart !== undefined && sourceEnd !== undefined) {\n diagnostics.push({\n ruleId: 'no-repetitive-sentence-startings',\n severity: 'warn',\n message: `${runLength} consecutive sentences start with \"${runWord}\" — vary the openings`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noRepetitiveSentenceStartings.help,\n })\n }\n }\n runWord = null\n runLength = 0\n runStart = index + 1\n continue\n }\n\n if (firstWord === runWord) {\n runLength++\n } else {\n if (runLength >= threshold && runWord) {\n const first = sentences[runStart]!\n const last = sentences[index - 1]!\n const sourceStart = sourceMap[first.start]\n const sourceEnd = sourceMap[last.end - 1]\n if (sourceStart !== undefined && sourceEnd !== undefined) {\n diagnostics.push({\n ruleId: 'no-repetitive-sentence-startings',\n severity: 'warn',\n message: `${runLength} consecutive sentences start with \"${runWord}\" — vary the openings`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noRepetitiveSentenceStartings.help,\n })\n }\n }\n runWord = firstWord\n runStart = index\n runLength = 1\n }\n }\n\n if (runLength >= threshold && runWord) {\n const first = sentences[runStart]!\n const last = sentences[sentences.length - 1]!\n const sourceStart = sourceMap[first.start]\n const sourceEnd = sourceMap[last.end - 1]\n if (sourceStart !== undefined && sourceEnd !== undefined) {\n diagnostics.push({\n ruleId: 'no-repetitive-sentence-startings',\n severity: 'warn',\n message: `${runLength} consecutive sentences start with \"${runWord}\" — vary the openings`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noRepetitiveSentenceStartings.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoFillerWordsOptions {\n words: string[]\n}\n\nconst DEFAULT_WORDS = ['just']\n\nexport const noFillerWords: Rule<NoFillerWordsOptions> = {\n id: 'no-filler-words',\n description: 'Ban filler words that pad out a sentence without adding meaning',\n defaults: { words: DEFAULT_WORDS },\n help: 'Filler words like \"just\" dilute your claim. ' +\n 'Remove the word; if the sentence then feels too blunt, ' +\n 'rewrite the surrounding copy instead of softening it.',\n\n check({ text, sourceMap, options }: RuleInput<NoFillerWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const words = options.words?.length ? options.words : DEFAULT_WORDS\n\n for (const word of words) {\n const escaped = word.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const re = new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-filler-words',\n severity: 'error',\n message: `remove \"${m[0].toLowerCase()}\" — it's filler`,\n range: { start, end },\n help: noFillerWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule } from '@faircopy/core'\nimport { noComplexSentences } from './no-complex-sentences.js'\nimport { noEmDash } from './no-em-dash.js'\nimport { noWeaselWords } from './no-weasel-words.js'\nimport { noRhetoricalScaffolding } from './no-rhetorical-scaffolding.js'\nimport { noNonInclusiveLanguage } from './no-non-inclusive-language.js'\nimport { noRedundantPhrases } from './no-redundant-phrases.js'\nimport { noPassiveVoice } from './no-passive-voice.js'\nimport { noCliches } from './no-cliches.js'\nimport { noRepetitiveSentenceStartings } from './no-repetitive-sentence-startings.js'\nimport { noFillerWords } from './no-filler-words.js'\n\nexport { noComplexSentences } from './no-complex-sentences.js'\nexport { noEmDash } from './no-em-dash.js'\nexport { noWeaselWords } from './no-weasel-words.js'\nexport { noRhetoricalScaffolding } from './no-rhetorical-scaffolding.js'\nexport { noNonInclusiveLanguage } from './no-non-inclusive-language.js'\nexport { noRedundantPhrases } from './no-redundant-phrases.js'\nexport { noPassiveVoice } from './no-passive-voice.js'\nexport { noCliches } from './no-cliches.js'\nexport { noRepetitiveSentenceStartings } from './no-repetitive-sentence-startings.js'\nexport { noFillerWords } from './no-filler-words.js'\nexport type { NoComplexSentencesOptions } from './no-complex-sentences.js'\nexport type { NoEmDashOptions } from './no-em-dash.js'\nexport type { NoWeaselWordsOptions } from './no-weasel-words.js'\nexport type { NoRhetoricalScaffoldingOptions } from './no-rhetorical-scaffolding.js'\nexport type { NoNonInclusiveLanguageOptions, NonInclusiveTerm } from './no-non-inclusive-language.js'\nexport type { NoRedundantPhrasesOptions, RedundantPhrase } from './no-redundant-phrases.js'\nexport type { NoPassiveVoiceOptions } from './no-passive-voice.js'\nexport type { NoClichesOptions, ClichePhrase } from './no-cliches.js'\nexport type { NoRepetitiveSentenceStartingsOptions } from './no-repetitive-sentence-startings.js'\nexport type { NoFillerWordsOptions } from './no-filler-words.js'\n\n/** All built-in rules keyed by their rule ID. */\nexport const ruleRegistry: Map<string, Rule> = new Map([\n ['no-complex-sentences', noComplexSentences as Rule],\n ['no-em-dash', noEmDash as Rule],\n ['no-weasel-words', noWeaselWords as Rule],\n ['no-rhetorical-scaffolding', noRhetoricalScaffolding as Rule],\n ['no-non-inclusive-language', noNonInclusiveLanguage as Rule],\n ['no-redundant-phrases', noRedundantPhrases as Rule],\n ['no-passive-voice', noPassiveVoice as Rule],\n ['no-cliches', noCliches as Rule],\n ['no-repetitive-sentence-startings', noRepetitiveSentenceStartings as Rule],\n ['no-filler-words', noFillerWords as Rule],\n])\n"],"mappings":"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","names":["match","escapeRegExp","DEFAULT_PHRASES","escapeRegex","buildPattern","DEFAULT_OPTIONS","getSentences","match","DEFAULT_WORDS"]}
1
+ {"version":3,"sources":["../src/no-complex-sentences.ts","../src/no-em-dash.ts","../src/no-weasel-words.ts","../src/no-rhetorical-scaffolding.ts","../src/no-non-inclusive-language.ts","../src/no-redundant-phrases.ts","../src/no-passive-voice.ts","../src/no-cliches.ts","../src/no-repetitive-sentence-startings.ts","../src/no-filler-words.ts","../src/index.ts"],"sourcesContent":["import type { Diagnostic, Rule, RuleInput, Suggestion } from '@faircopy/core'\n\nexport interface NoComplexSentencesOptions {\n /** Target Flesch-Kincaid grade level. Sentences scoring above this are flagged. */\n maxGradeLevel?: number\n /** Minimum words a sentence must contain before it is scored. Shorter sentences are too noisy. */\n minWords?: number\n}\n\nconst DEFAULT_OPTIONS: Required<NoComplexSentencesOptions> = {\n maxGradeLevel: 12,\n minWords: 10,\n}\n\nexport const noComplexSentences: Rule<NoComplexSentencesOptions> = {\n id: 'no-complex-sentences',\n description: 'Flag individual sentences whose Flesch-Kincaid grade level exceeds a target',\n defaults: { ...DEFAULT_OPTIONS },\n help: 'Long, syllable-dense sentences are hard to read. Break them into shorter sentences that each make one point.',\n\n check({ text, sourceMap, options }: RuleInput<NoComplexSentencesOptions>): Diagnostic[] {\n const maxGradeLevel = options.maxGradeLevel ?? DEFAULT_OPTIONS.maxGradeLevel\n const minWords = options.minWords ?? DEFAULT_OPTIONS.minWords\n\n const diagnostics: Diagnostic[] = []\n\n for (const { sentence, start, end } of getSentences(text)) {\n const words = getWords(sentence)\n if (words.length < minWords || words.length === 0) continue\n\n const syllables = words.reduce((sum, word) => sum + countSyllables(word), 0)\n const grade = fleschKincaidGrade(words.length, 1, syllables)\n\n if (grade <= maxGradeLevel) continue\n\n const sourceStart = sourceMap[start]\n const sourceEnd = sourceMap[end - 1]\n if (sourceStart === undefined || sourceEnd === undefined) continue\n\n const roundedGrade = Math.round(grade * 10) / 10\n const suggest: Suggestion = {\n description: 'Split this sentence into shorter sentences, one idea each.',\n edits: [],\n }\n\n diagnostics.push({\n ruleId: 'no-complex-sentences',\n severity: 'warn',\n message: `sentence readability is grade ${roundedGrade.toFixed(1)} — simplify to ${maxGradeLevel} or below`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noComplexSentences.help,\n suggest,\n })\n }\n\n return diagnostics\n },\n}\n\nfunction getSentences(text: string): Array<{ sentence: string; start: number; end: number }> {\n const sentences: Array<{ sentence: string; start: number; end: number }> = []\n const abbreviationPattern = /\\b(?:dr|mr|mrs|ms|prof|sr|jr|eg|ie|etc|vs|vol|fig|no)\\.|\\b(?:a|p)\\.m\\./gi\n const placeholder = '\\u0000'\n const masked = text.replace(abbreviationPattern, (match, offset) => {\n // a.m./p.m. may use their trailing period as a sentence terminator. Keep it\n // when followed by whitespace and an uppercase letter or end of string.\n if (/\\b(?:a|p)\\.m\\.$/i.test(match)) {\n const after = text.slice(offset + match.length)\n if (/^\\s+(?:[A-Z]|$)/.test(after)) {\n return match[0] + placeholder + match.slice(2)\n }\n }\n return match.replaceAll('.', placeholder)\n })\n\n const terminator = /[.!?]+/g\n let lastEnd = 0\n let match: RegExpExecArray | null\n\n while ((match = terminator.exec(masked)) !== null) {\n const end = match.index + match[0].length\n const sentence = masked.slice(lastEnd, end).replaceAll(placeholder, '.')\n const trimmed = sentence.trimStart()\n const leadingSpace = sentence.length - trimmed.length\n sentences.push({ sentence: trimmed, start: lastEnd + leadingSpace, end })\n lastEnd = end\n }\n\n return sentences\n}\n\nfunction getWords(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9\\s'-]/g, ' ')\n .split(/\\s+/)\n .filter(word => word.length > 0 && /[a-z0-9]/.test(word))\n}\n\nfunction countSyllables(word: string): number {\n const cleaned = word.toLowerCase().replace(/[^a-z]/g, '')\n if (!cleaned) return 0\n if (cleaned.length <= 3) return 1\n\n const vowels = cleaned.match(/[aeiouy]+/g)\n if (!vowels) return 1\n\n let count = vowels.length\n if (cleaned.endsWith('e')) count--\n if (cleaned.endsWith('le') && cleaned.length > 2 && !/[aeiouy]$/.test(cleaned[cleaned.length - 3] ?? '')) {\n count++\n }\n return Math.max(1, count)\n}\n\nfunction fleschKincaidGrade(words: number, sentences: number, syllables: number): number {\n if (sentences === 0 || words === 0) return 0\n return 0.39 * (words / sentences) + 11.8 * (syllables / words) - 15.59\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoEmDashOptions {\n /** Additionally flag en-dashes (U+2013). Default false. */\n flagEnDash?: boolean\n /** Additionally flag ASCII double-hyphen --. Default false. */\n flagDoubleHyphen?: boolean\n}\n\nexport const noEmDash: Rule<NoEmDashOptions> = {\n id: 'no-em-dash',\n description: 'Ban the em-dash character in marketing copy',\n defaults: { flagEnDash: false, flagDoubleHyphen: false },\n help: 'Em-dashes are a stylistic tell. Split the sentence at the break. ' +\n 'Use a period, a semicolon, parentheses, or a new sentence. ' +\n 'If the clauses genuinely belong together and a comma reads worse, write shorter sentences.',\n\n check({ text, sourceMap, options }: RuleInput<NoEmDashOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const opts = { ...noEmDash.defaults, ...options }\n\n const flag = (re: RegExp, message: string) => {\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({ ruleId: 'no-em-dash', severity: 'error', message, range: { start, end }, help: noEmDash.help })\n }\n }\n\n flag(/—/g, 'use a sentence break instead of an em-dash')\n if (opts.flagEnDash) flag(/–/g, 'use a hyphen instead of an en-dash')\n if (opts.flagDoubleHyphen) flag(/--/g, 'use a sentence break instead of --')\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoWeaselWordsOptions {\n words: string[]\n}\n\nconst DEFAULT_WORDS = ['actually', 'truly', 'really', 'literally']\n\nexport const noWeaselWords: Rule<NoWeaselWordsOptions> = {\n id: 'no-weasel-words',\n description: 'Ban reinforcement adverbs that protest too much',\n defaults: { words: DEFAULT_WORDS },\n help: 'Reinforcement adverbs defend a claim instead of making it. ' +\n 'Delete the word. If the sentence no longer reads right, ' +\n 'the original claim was the problem — rewrite it, don\\'t prop it up.',\n\n check({ text, sourceMap, options }: RuleInput<NoWeaselWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const words = options.words?.length ? options.words : DEFAULT_WORDS\n\n for (const word of words) {\n const re = new RegExp(`\\\\b${word}\\\\b`, 'gi')\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-weasel-words',\n severity: 'error',\n message: `remove \"${m[0].toLowerCase()}\" — it weakens the claim`,\n range: { start, end },\n help: noWeaselWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoRhetoricalScaffoldingOptions {\n /** Disable \"X is Y, not Z\" detection. Default false. */\n allowIsNotConstruction?: boolean\n /** Disable \"Without X / With X\" detection. Default false. */\n allowWithoutWithConstruction?: boolean\n /** Additional banned patterns as regex strings. */\n extraPatterns?: string[]\n}\n\n// \"X is Y, not a/an/the/just/only/merely/simply...\"\nconst IS_NOT_RE = /\\b(is|are|was|were)\\s+[^.!?]{1,80},\\s+not\\s+(a|an|the|just|only|merely|simply)\\b/gi\n\n// \"Without ... [sentences] ... With ...\"\nconst WITHOUT_WITH_RE = /\\bWithout\\b[^.!?]{1,200}[.!?]\\s*(?:[^.!?]{1,200}[.!?]\\s*){0,2}With\\b/gs\n\nexport const noRhetoricalScaffolding: Rule<NoRhetoricalScaffoldingOptions> = {\n id: 'no-rhetorical-scaffolding',\n description: 'Ban formulaic \"X is Y, not Z\" and \"Without X / With X\" patterns',\n defaults: { allowIsNotConstruction: false, allowWithoutWithConstruction: false, extraPatterns: [] },\n help: 'These patterns spend a clause denying a straw man or performing a reveal instead of making a claim. ' +\n 'Delete the setup and keep the claim.',\n\n check({ text, sourceMap, options }: RuleInput<NoRhetoricalScaffoldingOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const opts = { ...noRhetoricalScaffolding.defaults, ...options }\n\n if (!opts.allowIsNotConstruction) {\n const re = new RegExp(IS_NOT_RE.source, IS_NOT_RE.flags)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-rhetorical-scaffolding',\n severity: 'error',\n message: 'avoid \"X is Y, not Z\" — state the claim directly',\n range: { start, end },\n help: noRhetoricalScaffolding.help,\n })\n }\n }\n\n if (!opts.allowWithoutWithConstruction) {\n const re = new RegExp(WITHOUT_WITH_RE.source, WITHOUT_WITH_RE.flags)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-rhetorical-scaffolding',\n severity: 'error',\n message: 'avoid \"Without X / With X\" — drop the setup and make the claim',\n range: { start, end },\n help: noRhetoricalScaffolding.help,\n })\n }\n }\n\n for (const pattern of opts.extraPatterns ?? []) {\n const re = new RegExp(pattern, 'gi')\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-rhetorical-scaffolding',\n severity: 'error',\n message: 'banned rhetorical pattern',\n range: { start, end },\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NonInclusiveTerm {\n term: string\n alternatives: string[]\n /** 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. */\n exact?: boolean\n}\n\nexport interface NoNonInclusiveLanguageOptions {\n /** Terms to flag with suggested alternatives. */\n terms?: NonInclusiveTerm[]\n /** Additional allowed terms that override defaults. */\n allowedTerms?: string[]\n}\n\nconst DEFAULT_TERMS: NonInclusiveTerm[] = [\n { term: 'guys', alternatives: ['everyone', 'team', 'folks'] },\n { term: 'manpower', alternatives: ['workforce', 'staffing', 'personnel'] },\n { term: 'whitelist', alternatives: ['allowlist'] },\n { term: 'blacklist', alternatives: ['denylist', 'blocklist'] },\n { term: 'master', alternatives: ['primary', 'main', 'leader'] },\n { term: 'slave', alternatives: ['secondary', 'replica', 'follower'] },\n { term: 'crazy', alternatives: ['unexpected', 'intense', 'extreme'] },\n { term: 'insane', alternatives: ['extreme', 'unbelievable', 'remarkable'] },\n { term: 'dumb', alternatives: ['unhelpful', 'poor', 'uninformed'] },\n { term: 'lame', alternatives: ['unimpressive', 'inadequate', 'weak'] },\n { term: 'sanity check', alternatives: ['quick check', 'confidence check', 'verification'], exact: true },\n { term: 'blind spot', alternatives: ['unaware area', 'gap', 'oversight'], exact: true },\n { term: 'grandfathered', alternatives: ['legacy status', 'exempted'] },\n { term: 'mankind', alternatives: ['humanity', 'humankind', 'people'] },\n]\n\nfunction escapeRegex(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPattern(term: string, exact: boolean): RegExp {\n const escaped = escapeRegex(term)\n const isPhrase = /\\s/.test(term)\n if (isPhrase) {\n if (exact) {\n return new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n }\n return new RegExp(escaped, 'gi')\n }\n return new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n}\n\nexport const noNonInclusiveLanguage: Rule<NoNonInclusiveLanguageOptions> = {\n id: 'no-non-inclusive-language',\n description: 'Flag non-inclusive terms and suggest neutral alternatives',\n defaults: { terms: DEFAULT_TERMS, allowedTerms: [] },\n help: 'Non-inclusive terms can alienate readers. Replace them with neutral alternatives that name the same idea without relying on identity, ability, or historical power metaphors.',\n\n check({ text, sourceMap, options }: RuleInput<NoNonInclusiveLanguageOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const terms = options.terms?.length ? options.terms : DEFAULT_TERMS\n const allowed = new Set((options.allowedTerms ?? []).map(term => term.toLowerCase()))\n\n for (const { term, alternatives, exact } of terms) {\n if (allowed.has(term.toLowerCase())) continue\n\n const re = buildPattern(term, exact ?? false)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n const suggestion = alternatives.join(', ')\n diagnostics.push({\n ruleId: 'no-non-inclusive-language',\n severity: 'error',\n message: `replace \"${m[0]}\" with a neutral alternative such as \"${suggestion}\"`,\n range: { start, end },\n help: noNonInclusiveLanguage.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput, Suggestion } from '@faircopy/core'\n\nexport interface RedundantPhrase {\n phrase: string\n replacement: string\n}\n\nexport interface NoRedundantPhrasesOptions {\n phrases?: RedundantPhrase[]\n}\n\nconst DEFAULT_PHRASES: RedundantPhrase[] = [\n { phrase: 'in order to', replacement: 'to' },\n { phrase: 'due to the fact that', replacement: 'because' },\n { phrase: 'in spite of the fact that', replacement: 'although' },\n { phrase: 'at this point in time', replacement: 'now' },\n { phrase: 'in the event that', replacement: 'if' },\n { phrase: 'for the purpose of', replacement: 'to' },\n { phrase: 'with regard to', replacement: 'about' },\n { phrase: 'in close proximity to', replacement: 'near' },\n { phrase: 'a large number of', replacement: 'many' },\n { phrase: 'the reason is that', replacement: 'because' },\n { phrase: 'in the vicinity of', replacement: 'near' },\n { phrase: 'on the occasion of', replacement: 'when' },\n { phrase: 'in view of the fact that', replacement: 'because' },\n { phrase: 'owing to the fact that', replacement: 'because' },\n { phrase: 'for the reason that', replacement: 'because' },\n { phrase: 'in light of the fact that', replacement: 'because' },\n { phrase: 'it is important to note that', replacement: '' },\n { phrase: 'it should be noted that', replacement: '' },\n { phrase: 'needless to say', replacement: '' },\n { phrase: 'it goes without saying that', replacement: '' },\n]\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPhrasePattern(phrase: string): RegExp {\n const escaped = escapeRegExp(phrase).replace(/\\\\s+/g, '\\\\s+')\n return new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n}\n\nexport const noRedundantPhrases: Rule<NoRedundantPhrasesOptions> = {\n id: 'no-redundant-phrases',\n description: 'Flag wordy redundant phrases and suggest concise replacements',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Redundant phrases pad copy with extra words that add no meaning. ' +\n 'Replace them with the concise alternative, or delete the phrase entirely ' +\n 'if the replacement is empty.',\n\n check({ text, sourceMap, options }: RuleInput<NoRedundantPhrasesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n\n for (const { phrase, replacement } of phrases) {\n const re = buildPhrasePattern(phrase)\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedPhrase = match[0]\n const start = sourceMap[match.index]!\n const end = sourceMap[match.index + matchedPhrase.length - 1]! + 1\n\n const suggest: Suggestion = {\n description: replacement\n ? `replace \"${matchedPhrase}\" with \"${replacement}\"`\n : `delete \"${matchedPhrase}\"`,\n edits: [{ range: { start, end }, replacement }],\n }\n\n diagnostics.push({\n ruleId: 'no-redundant-phrases',\n severity: 'warn',\n message: replacement\n ? `\"${matchedPhrase}\" is redundant — use \"${replacement}\"`\n : `\"${matchedPhrase}\" is redundant — delete it`,\n range: { start, end },\n help: noRedundantPhrases.help,\n suggest,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoPassiveVoiceOptions {\n /** Auxiliary verbs that can introduce a passive construction. */\n auxiliaries?: string[]\n /** Past participles to flag when preceded by an auxiliary. */\n participles?: string[]\n /** Phrases to allow even if they match the passive pattern. */\n allowedPhrases?: string[]\n}\n\nconst DEFAULT_AUXILIARIES = ['is', 'are', 'was', 'were', 'be', 'been', 'being']\n\nconst DEFAULT_PARTICIPLES = [\n 'accepted', 'accomplished', 'achieved', 'acquired', 'added', 'addressed', 'adjusted', 'admired',\n 'admitted', 'adopted', 'advanced', 'affected', 'afforded', 'agreed', 'allowed', 'announced',\n 'answered', 'anticipated', 'approved', 'arranged', 'asked', 'assembled', 'assessed', 'assigned',\n 'assisted', 'assumed', 'assured', 'attached', 'attacked', 'attempted', 'attended', 'attracted',\n 'avoided', 'awarded', 'based', 'beaten', 'become', 'begun', 'believed', 'belonged', 'benefited',\n 'betrayed', 'blamed', 'blessed', 'blocked', 'blown', 'boarded', 'boiled', 'booked', 'borrowed',\n 'bothered', 'bought', 'bound', 'branded', 'broken', 'brought', 'built', 'burned', 'burst',\n 'called', 'captured', 'carried', 'caused', 'caught', 'celebrated', 'challenged', 'changed',\n 'charged', 'chased', 'checked', 'chosen', 'claimed', 'cleaned', 'cleared', 'clicked', 'climbed',\n 'closed', 'coached', 'collected', 'combined', 'come', 'comforted', 'committed', 'communicated',\n 'compared', 'competed', 'completed', 'complicated', 'composed', 'computed', 'conceived',\n 'concentrated', 'concerned', 'concluded', 'conditioned', 'conducted', 'confirmed', 'connected',\n 'considered', 'consisted', 'constructed', 'consulted', 'consumed', 'contacted', 'contained',\n 'continued', 'contributed', 'controlled', 'converted', 'convinced', 'cooked', 'cost', 'counted',\n 'covered', 'created', 'crossed', 'crowded', 'crushed', 'cried', 'cut', 'damaged', 'danced',\n 'dated', 'dealt', 'decided', 'declared', 'declined', 'decorated', 'decreased', 'defeated',\n 'defended', 'defined', 'delayed', 'delivered', 'demanded', 'demonstrated', 'denied', 'departed',\n 'depended', 'described', 'deserved', 'designed', 'destroyed', 'detailed', 'detected', 'determined',\n 'developed', 'devoted', 'differed', 'digested', 'diminished', 'directed', 'discovered', 'discussed',\n 'displayed', 'distributed', 'disturbed', 'divided', 'done', 'doubled', 'doubted', 'drafted',\n 'dragged', 'drawn', 'dressed', 'driven', 'dropped', 'drowned', 'dug', 'earned', 'eaten',\n 'edited', 'educated', 'elected', 'eliminated', 'embarrassed', 'emerged', 'employed', 'enabled',\n 'encouraged', 'ended', 'engaged', 'engineered', 'enjoyed', 'entered', 'entertained', 'equipped',\n 'escaped', 'established', 'estimated', 'evaluated', 'evolved', 'examined', 'exceeded', 'exchanged',\n 'excited', 'excused', 'executed', 'exercised', 'exhausted', 'exhibited', 'expanded', 'expected',\n 'experienced', 'explained', 'exploded', 'explored', 'exported', 'exposed', 'expressed', 'extended',\n 'faced', 'failed', 'fallen', 'favored', 'feared', 'featured', 'fed', 'felt', 'fetched',\n 'fielded', 'filled', 'filmed', 'filtered', 'financed', 'finished', 'fired', 'fitted', 'fixed',\n 'flashed', 'flown', 'focused', 'folded', 'followed', 'forced', 'forgotten', 'formed', 'founded',\n 'framed', 'freed', 'frozen', 'frustrated', 'fueled', 'fulfilled', 'functioned', 'funded',\n 'gained', 'gathered', 'given', 'gone', 'governed', 'grabbed', 'graded', 'granted', 'greeted',\n 'grown', 'guaranteed', 'guarded', 'guessed', 'guided', 'handled', 'hanged', 'happened', 'harmed',\n 'harvested', 'hated', 'headed', 'healed', 'heard', 'heated', 'helped', 'hidden', 'highlighted',\n 'hired', 'hit', 'held', 'honored', 'hooked', 'hoped', 'hosted', 'hunted', 'hurried', 'hurt',\n 'identified', 'ignored', 'illustrated', 'imagined', 'implemented', 'implied', 'imported',\n 'imposed', 'impressed', 'improved', 'included', 'increased', 'indicated', 'influenced', 'informed',\n 'initiated', 'injured', 'inquired', 'inserted', 'inspected', 'inspired', 'installed', 'instructed',\n 'intended', 'interacted', 'interested', 'interrupted', 'interviewed', 'introduced', 'invented',\n 'invested', 'investigated', 'invited', 'involved', 'isolated', 'issued', 'joined', 'judged',\n 'jumped', 'justified', 'kept', 'kicked', 'killed', 'kissed', 'knocked', 'known', 'labeled',\n 'lacked', 'landed', 'lasted', 'launched', 'learned', 'leased', 'left', 'lent', 'let', 'licensed',\n 'lifted', 'lighted', 'liked', 'limited', 'linked', 'listed', 'listened', 'lived', 'loaded',\n 'located', 'locked', 'logged', 'looked', 'lost', 'loved', 'made', 'maintained', 'managed',\n 'manufactured', 'marked', 'marketed', 'married', 'mastered', 'matched', 'mattered', 'matured',\n 'meant', 'measured', 'met', 'mentioned', 'merged', 'messed', 'migrated', 'minded', 'missed',\n 'mixed', 'modified', 'monitored', 'moved', 'multiplied', 'named', 'narrowed', 'needed',\n 'negotiated', 'noted', 'noticed', 'obtained', 'occurred', 'offered', 'opened', 'operated',\n 'opposed', 'ordered', 'organized', 'oriented', 'originated', 'overcome', 'overlooked', 'owned',\n 'paced', 'packed', 'paid', 'painted', 'paired', 'parked', 'participated', 'passed', 'patented',\n 'paused', 'perceived', 'performed', 'permitted', 'persuaded', 'phased', 'picked', 'pictured',\n 'placed', 'planned', 'planted', 'played', 'pleased', 'plugged', 'pointed', 'polished', 'popped',\n 'possessed', 'posted', 'poured', 'powered', 'praised', 'prayed', 'preached', 'preceded',\n 'predicted', 'preferred', 'prepared', 'prescribed', 'presented', 'preserved', 'pressed', 'pretended',\n 'prevented', 'priced', 'printed', 'prioritized', 'processed', 'produced', 'profited', 'programmed',\n 'prohibited', 'promised', 'promoted', 'prompted', 'proposed', 'protected', 'proved', 'provided',\n 'published', 'pulled', 'pumped', 'punched', 'purchased', 'pursued', 'pushed', 'put', 'qualified',\n 'questioned', 'quit', 'quoted', 'raised', 'ranked', 'rated', 'reached', 'reacted', 'read',\n 'realized', 'received', 'recognized', 'recommended', 'reconciled', 'recorded', 'recovered',\n 'recruited', 'reduced', 'referred', 'reflected', 'refused', 'regarded', 'regulated', 'rejected',\n 'related', 'released', 'remained', 'remembered', 'reminded', 'removed', 'rendered', 'renewed',\n 'rented', 'repaired', 'repeated', 'replaced', 'replied', 'reported', 'represented', 'reproduced',\n 'requested', 'required', 'researched', 'reserved', 'resolved', 'respected', 'responded', 'restored',\n 'resulted', 'retained', 'retired', 'retrieved', 'returned', 'revealed', 'reviewed', 'revised',\n 'revived', 'rewarded', 'ridden', 'risen', 'rolled', 'rooted', 'rounded', 'ruled', 'run', 'rushed',\n 'sacrificed', 'said', 'sold', 'sampled', 'saved', 'scanned', 'scared', 'scheduled', 'scored',\n 'scraped', 'scratched', 'screened', 'searched', 'seasoned', 'seated', 'secured', 'seen', 'selected',\n 'sent', 'separated', 'served', 'serviced', 'set', 'settled', 'settled', 'shaped', 'shared',\n 'shocked', 'shaken', 'shaped', 'shipped', 'shocked', 'shot', 'shown', 'shut', 'signed', 'simplified',\n 'singled', 'sited', 'situated', 'sized', 'sketched', 'skilled', 'slammed', 'slashed', 'slid',\n 'slipped', 'slowed', 'smashed', 'smelled', 'smiled', 'smoked', 'snapped', 'soaked', 'sold',\n 'solved', 'sorted', 'sought', 'sounded', 'spared', 'sparked', 'spawned', 'spearheaded', 'specified',\n 'spent', 'spilled', 'spun', 'split', 'spoken', 'sponsored', 'spotted', 'spread', 'sprung',\n 'staged', 'stained', 'staked', 'stalled', 'stamped', 'started', 'stated', 'stationed', 'stayed',\n 'stolen', 'stepped', 'sticked', 'stimulated', 'stirred', 'stopped', 'stored', 'strained',\n 'streamed', 'strengthened', 'stressed', 'stretched', 'stricken', 'struck', 'structured',\n 'struggled', 'studied', 'stuffed', 'styled', 'submitted', 'substituted', 'succeeded', 'sucked',\n 'sued', 'suffered', 'suggested', 'suited', 'summed', 'supplied', 'supported', 'supposed',\n 'surprised', 'surrounded', 'surveyed', 'survived', 'suspected', 'suspended', 'sustained', 'swallowed',\n 'swapped', 'swept', 'swelled', 'swung', 'switched', 'tackled', 'tagged', 'taken', 'talked',\n 'tapped', 'targeted', 'tasted', 'taught', 'torn', 'tested', 'testified', 'texted', 'thanked',\n 'thrown', 'thrust', 'ticked', 'tied', 'tightened', 'timed', 'tipped', 'tired', 'titled',\n 'tolerated', 'topped', 'touched', 'toured', 'tracked', 'traded', 'trained', 'transferred',\n 'transformed', 'translated', 'transmitted', 'transported', 'trapped', 'traveled', 'treated',\n 'trimmed', 'tripled', 'triumphed', 'troubled', 'trusted', 'tried', 'turned', 'twisted', 'typed',\n 'undergone', 'understood', 'undertaken', 'unfolded', 'unified', 'united', 'updated', 'upgraded',\n 'upheld', 'upset', 'used', 'utilized', 'valued', 'vanished', 'varied', 'verified', 'vetoed',\n 'viewed', 'visited', 'voiced', 'voted', 'waged', 'waited', 'walked', 'wandered', 'wanted',\n 'warned', 'warranted', 'washed', 'wasted', 'watched', 'weakened', 'worn', 'welcomed', 'won',\n 'wondered', 'worked', 'worried', 'worshiped', 'wounded', 'written', 'wrung', 'yielded',\n]\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPassivePattern(auxiliaries: string[], participles: string[]): RegExp {\n const auxPattern = auxiliaries.map(escapeRegExp).join('|')\n const participlePattern = participles.map(escapeRegExp).join('|')\n return new RegExp(`\\\\b(${auxPattern})\\\\s+(\\\\w+\\\\s+){0,3}(${participlePattern})\\\\b`, 'gi')\n}\n\nexport const noPassiveVoice: Rule<NoPassiveVoiceOptions> = {\n id: 'no-passive-voice',\n description: 'Flag likely passive-voice constructions using auxiliary + past participle patterns',\n defaults: {\n auxiliaries: DEFAULT_AUXILIARIES,\n participles: DEFAULT_PARTICIPLES,\n allowedPhrases: [],\n },\n help: 'Passive voice often hides the actor and adds drag. ' +\n 'Prefer naming who did the action unless the actor genuinely does not matter.',\n\n check({ text, sourceMap, options }: RuleInput<NoPassiveVoiceOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const auxiliaries = options.auxiliaries?.length ? options.auxiliaries : DEFAULT_AUXILIARIES\n const participles = options.participles?.length ? options.participles : DEFAULT_PARTICIPLES\n const allowed = new Set((options.allowedPhrases ?? []).map(phrase => phrase.toLowerCase()))\n\n const re = buildPassivePattern(auxiliaries, participles)\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedText = match[0]\n const lowerMatch = matchedText.toLowerCase()\n\n let allowedMatch = false\n for (const phrase of allowed) {\n if (lowerMatch.includes(phrase.toLowerCase())) {\n allowedMatch = true\n break\n }\n }\n if (allowedMatch) continue\n\n const start = sourceMap[match.index]!\n const end = sourceMap[match.index + matchedText.length - 1]! + 1\n\n diagnostics.push({\n ruleId: 'no-passive-voice',\n severity: 'warn',\n message: `rewrite passive construction \"${matchedText}\" with a named actor`,\n range: { start, end },\n help: noPassiveVoice.help,\n })\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface ClichePhrase {\n phrase: string\n alternatives: string[]\n}\n\nexport interface NoClichesOptions {\n /** Phrases to flag with suggested alternatives. Replaces the default list. */\n phrases?: ClichePhrase[]\n /** Default phrases to allow. */\n allow?: string[]\n}\n\nconst DEFAULT_PHRASES: ClichePhrase[] = [\n { phrase: 'world-class', alternatives: ['top-tier', 'exceptional', 'outstanding'] },\n { phrase: 'best-in-class', alternatives: ['leading', 'top-performing', 'category-leading'] },\n { phrase: 'cutting-edge', alternatives: ['advanced', 'modern', 'latest'] },\n { phrase: 'state-of-the-art', alternatives: ['advanced', 'modern', 'sophisticated'] },\n { phrase: 'game changer', alternatives: ['breakthrough', 'transformation', 'major advance'] },\n { phrase: 'game-changing', alternatives: ['transformative', 'breakthrough', 'revolutionary'] },\n { phrase: 'think outside the box', alternatives: ['be creative', 'innovate', 'find a new approach'] },\n { phrase: 'at the end of the day', alternatives: ['ultimately', 'finally', 'in summary'] },\n { phrase: 'low-hanging fruit', alternatives: ['easy wins', 'quick opportunities', 'simple targets'] },\n { phrase: 'move the needle', alternatives: ['make a measurable difference', 'drive results', 'create impact'] },\n { phrase: 'circle back', alternatives: ['follow up', 'reconnect', 'return to this'] },\n { phrase: 'give 110%', alternatives: ['do your best', 'make a full effort', 'go all in'] },\n { phrase: 'hit the ground running', alternatives: ['start quickly', 'get started immediately', 'begin effectively'] },\n { phrase: 'boil the ocean', alternatives: ['take on too much', 'overcomplicate', 'lose focus'] },\n { phrase: 'paradigm shift', alternatives: ['fundamental change', 'new approach', 'transformation'] },\n { phrase: 'next level', alternatives: ['advanced', 'improved', 'elevated'] },\n { phrase: 'seamless', alternatives: ['smooth', 'effortless', 'frictionless'] },\n { phrase: 'robust', alternatives: ['strong', 'resilient', 'reliable'] },\n { phrase: 'leverage', alternatives: ['use', 'take advantage of', 'utilize'] },\n { phrase: 'synergy', alternatives: ['collaboration', 'combined effect', 'partnership'] },\n]\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction buildPattern(phrase: string): RegExp {\n const escaped = escapeRegex(phrase)\n return new RegExp(`(?<!\\\\w)${escaped}(?!\\\\w)`, 'gi')\n}\n\nexport const noCliches: Rule<NoClichesOptions> = {\n id: 'no-cliches',\n description: 'Flag overused or clichéd phrases and suggest fresher alternatives',\n defaults: { phrases: DEFAULT_PHRASES, allow: [] },\n help: 'Clichés and overused phrases make copy feel generic and forgettable. ' +\n 'Replace them with specific, concrete language that reflects your actual product or idea.',\n\n check({ text, sourceMap, options }: RuleInput<NoClichesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n const allowed = new Set((options.allow ?? []).map(phrase => phrase.toLowerCase()))\n\n for (const { phrase, alternatives } of phrases) {\n if (allowed.has(phrase.toLowerCase())) continue\n\n const re = buildPattern(phrase)\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedPhrase = match[0]\n const start = sourceMap[match.index]!\n const end = sourceMap[match.index + matchedPhrase.length - 1]! + 1\n const suggestion = alternatives.join(', ')\n\n diagnostics.push({\n ruleId: 'no-cliches',\n severity: 'warn',\n message: `replace \"${matchedPhrase}\" with a fresher alternative such as \"${suggestion}\"`,\n range: { start, end },\n help: noCliches.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoRepetitiveSentenceStartingsOptions {\n /** Number of consecutive sentences that must share the same starting word before flagging. */\n threshold?: number\n /** Minimum sentence length (in words) to count. Very short sentences are ignored. */\n minWords?: number\n /** Words or phrases that are allowed to start multiple sentences. */\n allow?: string[]\n}\n\nconst DEFAULT_OPTIONS: Required<NoRepetitiveSentenceStartingsOptions> = {\n threshold: 3,\n minWords: 3,\n allow: ['the', 'a', 'an', 'it', 'this', 'that'],\n}\n\nfunction getSentences(text: string): Array<{ sentence: string; start: number; end: number }> {\n const sentences: Array<{ sentence: string; start: number; end: number }> = []\n const abbreviationPattern = /\\b(?:dr|mr|mrs|ms|prof|sr|jr|eg|ie|etc|vs|vol|fig|no)\\.|\\b(?:a|p)\\.m\\./gi\n const placeholder = '\\u0000'\n const masked = text.replace(abbreviationPattern, (match, offset) => {\n if (/\\b(?:a|p)\\.m\\.$/i.test(match)) {\n const after = text.slice(offset + match.length)\n if (/^\\s+(?:[A-Z]|$)/.test(after)) {\n return match[0] + placeholder + match.slice(2)\n }\n }\n return match.replaceAll('.', placeholder)\n })\n\n const terminator = /[.!?]+/g\n let lastEnd = 0\n let match: RegExpExecArray | null\n\n while ((match = terminator.exec(masked)) !== null) {\n const end = match.index + match[0].length\n const sentence = masked.slice(lastEnd, end).replaceAll(placeholder, '.')\n const trimmed = sentence.trimStart()\n const leadingSpace = sentence.length - trimmed.length\n sentences.push({ sentence: trimmed, start: lastEnd + leadingSpace, end })\n lastEnd = end\n }\n\n const trailing = masked.slice(lastEnd).trim()\n if (trailing) {\n sentences.push({ sentence: trailing, start: lastEnd, end: text.length })\n }\n\n return sentences\n}\n\nfunction getFirstWord(sentence: string): string | null {\n const match = sentence.trim().match(/^[a-zA-Z0-9]+/)\n return match ? match[0].toLowerCase() : null\n}\n\nfunction countWords(sentence: string): number {\n return sentence\n .replace(/[^a-zA-Z0-9\\s'-]/g, ' ')\n .split(/\\s+/)\n .filter(word => word.length > 0 && /[a-zA-Z0-9]/.test(word))\n .length\n}\n\nexport const noRepetitiveSentenceStartings: Rule<NoRepetitiveSentenceStartingsOptions> = {\n id: 'no-repetitive-sentence-startings',\n description: 'Flag consecutive sentences that start with the same word',\n defaults: { ...DEFAULT_OPTIONS },\n help: 'Starting several consecutive sentences with the same word creates a repetitive rhythm. ' +\n 'Vary the sentence openings or combine related sentences to keep the reader engaged.',\n\n check({ text, sourceMap, options }: RuleInput<NoRepetitiveSentenceStartingsOptions>): Diagnostic[] {\n const threshold = options.threshold ?? DEFAULT_OPTIONS.threshold\n const minWords = options.minWords ?? DEFAULT_OPTIONS.minWords\n const allowed = new Set((options.allow ?? DEFAULT_OPTIONS.allow).map(word => word.toLowerCase()))\n\n const diagnostics: Diagnostic[] = []\n const sentences = getSentences(text)\n\n let runStart = 0\n let runWord: string | null = null\n let runLength = 0\n\n for (let index = 0; index < sentences.length; index++) {\n const { sentence, start, end } = sentences[index]!\n const firstWord = getFirstWord(sentence)\n const words = countWords(sentence)\n\n if (!firstWord || words < minWords || allowed.has(firstWord)) {\n if (runLength >= threshold && runWord) {\n const first = sentences[runStart]!\n const last = sentences[index - 1]!\n const sourceStart = sourceMap[first.start]\n const sourceEnd = sourceMap[last.end - 1]\n if (sourceStart !== undefined && sourceEnd !== undefined) {\n diagnostics.push({\n ruleId: 'no-repetitive-sentence-startings',\n severity: 'warn',\n message: `${runLength} consecutive sentences start with \"${runWord}\" — vary the openings`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noRepetitiveSentenceStartings.help,\n })\n }\n }\n runWord = null\n runLength = 0\n runStart = index + 1\n continue\n }\n\n if (firstWord === runWord) {\n runLength++\n } else {\n if (runLength >= threshold && runWord) {\n const first = sentences[runStart]!\n const last = sentences[index - 1]!\n const sourceStart = sourceMap[first.start]\n const sourceEnd = sourceMap[last.end - 1]\n if (sourceStart !== undefined && sourceEnd !== undefined) {\n diagnostics.push({\n ruleId: 'no-repetitive-sentence-startings',\n severity: 'warn',\n message: `${runLength} consecutive sentences start with \"${runWord}\" — vary the openings`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noRepetitiveSentenceStartings.help,\n })\n }\n }\n runWord = firstWord\n runStart = index\n runLength = 1\n }\n }\n\n if (runLength >= threshold && runWord) {\n const first = sentences[runStart]!\n const last = sentences[sentences.length - 1]!\n const sourceStart = sourceMap[first.start]\n const sourceEnd = sourceMap[last.end - 1]\n if (sourceStart !== undefined && sourceEnd !== undefined) {\n diagnostics.push({\n ruleId: 'no-repetitive-sentence-startings',\n severity: 'warn',\n message: `${runLength} consecutive sentences start with \"${runWord}\" — vary the openings`,\n range: { start: sourceStart, end: sourceEnd + 1 },\n help: noRepetitiveSentenceStartings.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule, RuleInput, Diagnostic } from '@faircopy/core'\n\nexport interface NoFillerWordsOptions {\n words: string[]\n}\n\nconst DEFAULT_WORDS = ['just']\n\nexport const noFillerWords: Rule<NoFillerWordsOptions> = {\n id: 'no-filler-words',\n description: 'Ban filler words that pad out a sentence without adding meaning',\n defaults: { words: DEFAULT_WORDS },\n help: 'Filler words like \"just\" dilute your claim. ' +\n 'Remove the word; if the sentence then feels too blunt, ' +\n 'rewrite the surrounding copy instead of softening it.',\n\n check({ text, sourceMap, options }: RuleInput<NoFillerWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const words = options.words?.length ? options.words : DEFAULT_WORDS\n\n for (const word of words) {\n const escaped = word.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const re = new RegExp(`\\\\b${escaped}\\\\b`, 'gi')\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const start = sourceMap[m.index]!\n const end = sourceMap[m.index + m[0].length - 1]! + 1\n diagnostics.push({\n ruleId: 'no-filler-words',\n severity: 'error',\n message: `remove \"${m[0].toLowerCase()}\" — it's filler`,\n range: { start, end },\n help: noFillerWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Rule } from '@faircopy/core'\nimport { noComplexSentences } from './no-complex-sentences.js'\nimport { noEmDash } from './no-em-dash.js'\nimport { noWeaselWords } from './no-weasel-words.js'\nimport { noRhetoricalScaffolding } from './no-rhetorical-scaffolding.js'\nimport { noNonInclusiveLanguage } from './no-non-inclusive-language.js'\nimport { noRedundantPhrases } from './no-redundant-phrases.js'\nimport { noPassiveVoice } from './no-passive-voice.js'\nimport { noCliches } from './no-cliches.js'\nimport { noRepetitiveSentenceStartings } from './no-repetitive-sentence-startings.js'\nimport { noFillerWords } from './no-filler-words.js'\n\nexport { noComplexSentences } from './no-complex-sentences.js'\nexport { noEmDash } from './no-em-dash.js'\nexport { noWeaselWords } from './no-weasel-words.js'\nexport { noRhetoricalScaffolding } from './no-rhetorical-scaffolding.js'\nexport { noNonInclusiveLanguage } from './no-non-inclusive-language.js'\nexport { noRedundantPhrases } from './no-redundant-phrases.js'\nexport { noPassiveVoice } from './no-passive-voice.js'\nexport { noCliches } from './no-cliches.js'\nexport { noRepetitiveSentenceStartings } from './no-repetitive-sentence-startings.js'\nexport { noFillerWords } from './no-filler-words.js'\nexport type { NoComplexSentencesOptions } from './no-complex-sentences.js'\nexport type { NoEmDashOptions } from './no-em-dash.js'\nexport type { NoWeaselWordsOptions } from './no-weasel-words.js'\nexport type { NoRhetoricalScaffoldingOptions } from './no-rhetorical-scaffolding.js'\nexport type { NoNonInclusiveLanguageOptions, NonInclusiveTerm } from './no-non-inclusive-language.js'\nexport type { NoRedundantPhrasesOptions, RedundantPhrase } from './no-redundant-phrases.js'\nexport type { NoPassiveVoiceOptions } from './no-passive-voice.js'\nexport type { NoClichesOptions, ClichePhrase } from './no-cliches.js'\nexport type { NoRepetitiveSentenceStartingsOptions } from './no-repetitive-sentence-startings.js'\nexport type { NoFillerWordsOptions } from './no-filler-words.js'\n\n/** All built-in rules keyed by their rule ID. */\nexport const ruleRegistry: Map<string, Rule> = new Map([\n ['no-complex-sentences', noComplexSentences as Rule],\n ['no-em-dash', noEmDash as Rule],\n ['no-weasel-words', noWeaselWords as Rule],\n ['no-rhetorical-scaffolding', noRhetoricalScaffolding as Rule],\n ['no-non-inclusive-language', noNonInclusiveLanguage as Rule],\n ['no-redundant-phrases', noRedundantPhrases as Rule],\n ['no-passive-voice', noPassiveVoice as Rule],\n ['no-cliches', noCliches as Rule],\n ['no-repetitive-sentence-startings', noRepetitiveSentenceStartings as Rule],\n ['no-filler-words', noFillerWords as Rule],\n])\n"],"mappings":"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","names":["match","escapeRegExp","DEFAULT_PHRASES","escapeRegex","buildPattern","DEFAULT_OPTIONS","getSentences","match","DEFAULT_WORDS"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faircopy/rules-default",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "Default ruleset for faircopy: no-complex-sentences, no-em-dash, no-weasel-words, no-rhetorical-scaffolding, no-non-inclusive-language, no-redundant-phrases, no-passive-voice, no-cliches, no-repetitive-sentence-startings, no-filler-words",
5
5
  "type": "module",
6
6
  "exports": {
@@ -20,7 +20,7 @@
20
20
  "prepublishOnly": "pnpm run build"
21
21
  },
22
22
  "dependencies": {
23
- "@faircopy/core": "1.18.0"
23
+ "@faircopy/core": "1.19.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/bun": "latest",